Cześć,
próbuję stworzyć class buildera, który rozpakuje mi się do parametrów template'a.
Powiedzmy, że mam taką klasę:
enum class PrinterOption
{
NotSet = 0,
ToStdout,
ToFile,
};
template< PrinterOption V1, int V2, int V3, bool V4, int V5 >
class Printer
{
public:
static void SomeHotFunction()
{
if( V1 == PrinterOption::ToStdout )
{
printf( "SomeHotFunction - PrinterOption::ToStdout, V2 = %d, V3 = %d, V4 = %d, V5 = %d\n", V2, V3, V4, V5 );
}
else if( V1 == PrinterOption::ToFile )
{
printf( "SomeHotFunction - PrinterOption::ToFile, V2 = %d, V3 = %d, V4 = %d, V5 = %d\n", V2, V3, V4, V5 );
}
else
{
abort();
}
}
};
i mam do niej buildera:
class PrinterBuilder
{
public:
constexpr PrinterBuilder()
{
}
constexpr PrinterBuilder &SetToStdout( int v2, int v3 )
{
m_v1 = PrinterOption::ToStdout;
m_v2 = v2;
m_v3 = v3;
return *this;
}
constexpr PrinterBuilder &SetToFile( bool v4, int v5 )
{
m_v1 = PrinterOption::ToFile;
m_v4 = v4;
m_v5 = v5;
return *this;
}
constexpr auto GetTuple() const
{
return std::make_tuple( m_v1, m_v2, m_v3, m_v4, m_v5 );
}
public:
PrinterOption m_v1 = PrinterOption::NotSet;
int m_v2 = 1;
int m_v3 = 4;
bool m_v4 = false;
int m_v5 = 10;
};
i przykładowe użycie:
int main()
{
static constexpr auto p1 = PrinterBuilder().SetToStdout( 100, 200 ).GetTuple();
using Printer1 = Printer< std::get< 0 >( p1 ), std::get< 1 >( p1 ), std::get< 2 >( p1 ), std::get< 3 >( p1 ), std::get< 4 >( p1 ) >;
Printer1::SomeHotFunction();
static constexpr auto p2 = PrinterBuilder().SetToFile( true, 10000 ).GetTuple();
using Printer2 = Printer< std::get< 0 >( p2 ), std::get< 1 >( p2 ), std::get< 2 >( p2 ), std::get< 3 >( p2 ), std::get< 4 >( p2 ) >;
Printer2::SomeHotFunction();
return 0;
}
Otóż potrzebuję generalnego rozwiązania do tego kawałka kodu:
static constexpr auto p2 = PrinterBuilder().SetToFile( true, 10000 ).GetTuple();
using Printer2 = Printer< std::get< 0 >( p2 ), std::get< 1 >( p2 ), std::get< 2 >( p2 ), std::get< 3 >( p2 ), std::get< 4 >( p2 ) >;
konkretnie to potrzebuję jakiejś magii, która sprawi, że parametry z std::tuple
rozpakują mi się w miejscu parametrów szablonowych.
Coś takiego:
static constexpr auto p2 = PrinterBuilder().SetToFile( true, 10000 ).GetTuple();
using Printer2 = Printer< MAGIA( p2 ) >;
A jakby dało się zrobić jeszcze coś takiego:
using Printer2 = Printer< MAGIA( PrinterBuilder().SetToFile( true, 10000 ) ) >;
To już w ogóle byłbym wniebowzięty.
Uprzedzając pytania, odpowiadam po co mi to w ogóle jest potrzebne.
Nie mogę przenieść parametrów szablonowych do składowej klasy, powiedzmy:
class Printer
{
PrinterOption V1;
int V2;
int V3;
bool V4;
int V5;
Bo wtedy ta funkcja:
static void SomeHotFunction()
{
if( V1 == PrinterOption::ToStdout )
będzie miała warunek w runtime.
Z parametrami szablonowymi ta funkcja wygląda bardzo prosto w asm:
call Printer<(PrinterOption)1, 100, 200, false, 10>::SomeHotFunction()
.string "SomeHotFunction - PrinterOption::ToStdout, V2 = %d, V3 = %d, V4 = %d, V5 = %d\n"
Printer<(PrinterOption)1, 100, 200, false, 10>::SomeHotFunction():
push rbp
mov rbp, rsp
mov r8d, 10
mov ecx, 0
mov edx, 200
mov esi, 100
mov edi, OFFSET FLAT:.LC0
mov eax, 0
call printf
nop
pop rbp
ret
Brak warunków, prosty kod do wykonania.
Parametry mogą być złożone, więc może być wiele warunków do nich i dodatkowo pamięć parametrów może nie być w cache. Wszystkie parametry są znane w czasie kompilacji, jedynie miejsc użycia klasy Printer<>
może być bardzo wiele.
Założeniem systemu jest to, że funkcja SomeHotFunction()
(która dodatkowo będzie zinlineowana) będzie wołana na serwerze przynajmniej 80000000 razy na sekundę na wielu wątkach przypiętych do rdzeni i nie da się tego uniknąć.
Czy to jest w ogóle możliwe, żeby to zrobić?
Godbolt: https://godbolt.org/z/ccYvrohG6
Usage examples: It’s especially useful when you need to create an object with lots of possible configuration options.
The Builder pattern can be recognized in a class, which has a single creation method and several methods to configure the resulting object. Builder methods often support chaining
https://refactoring.guru/design-patterns/builder/php/example Wydało mi się to najbliżej tej idei. Generalnie tych opcji konfiguracji może być sporo i dlatego raczej potrzebuję buildera (choć widzę też inne opcje, ale mniej przyjazne dla usera frameworka).