I want consumers to use a Factory
for the construction of special derivations of a Scheme
.
Mandatorily, when asking a method from the factory, it returns a Table
specialization. A method implementation comprises of two things:
Table
specialization instance returned by Factory
Scheme
specialization that is complient with the Table
specialization type.The point of this whole question is convenience, in that I do not want to force consumers at knowing what scheme is compliant to which table, not even to mention what table type a given factory method returns. And lastly, I still want advanced users to still be able to force-specify the Scheme
specialization to be used (that's why factory only returns a table and not a full method; in that vain, I do not want to employ arguments in any factory routines).
Because I cannot use dynamic memory, I employ a template Scheme_handle
class that can hold an instance of a Scheme
specialization. But this defies its own purpose when the consumer is still demanded to know the necessary template type.
Below is my attempt.
#include<iostream>
//#include<memory> // infeasible since dynamic allocation
struct Table{ double data; };
struct Scheme{};
//
struct Table_A: Table{};
struct Table_B: Table{}; // there are too many different scheme types for use of std::conditional_t
//
struct Scheme_A: Scheme{
Scheme_A(Table_A const& t){}
};
struct Scheme_B: Scheme{
Scheme_B(Table_A const& t){}
Scheme_B(Table_B const& t){}
};
struct Factory{
/* factory implementation ... */
static Table_A make_x(){ return Table_A{}; } // it is mandatory that Factory returns Tables
static Table_B make_y(){ return Table_B{}; } // otherwise, Table would not have been
static Table_A make_z(){ return Table_A{}; } // included in this minimum code example
};
// specializations still need a declaration in 2023
template<typename TableType> class Scheme_handle;
// the following two lines of terrible boilerplate are necessary
// because c++ wont allow template<> using Scheme_handle<Table_A> = Scheme_A;
// same goes for typedef
template<> class Scheme_handle<Table_A>: public Scheme_A{ public: Scheme_handle(Table_A const& t):Scheme_A(t){} }; // Table_A -> Scheme_A
template<> class Scheme_handle<Table_B>: public Scheme_B{ public: Scheme_handle(Table_B const& t):Scheme_B(t){} }; // Table_B -> Scheme_B
// here is the consumer code that I need to be handable
struct MyClass{
//
// what more needs the compiler so one can drop the "<Table_A>" ?
Scheme_handle<Table_A> x1 = Factory::make_x(); // at least this gives me a handle to a scheme
//
Scheme_handle<Table_B> y1 = Factory::make_y(); // the consumer does not possibly want to remember the scheme type of y.
Scheme_handle<Table_B> y2 = Factory::make_y(); // schemes arent singletons!
//
Scheme_B x2 = Factory::make_x(); // a table does not imply a scheme!
// (but for each table there is a default compliant scheme, as defined by the mapping Scheme_handle : TableType -> SchemeType )
};
int main(){
MyClass q;
}
I assume what I want to do is pretty standard. How is it supposed to be done?, i.e. how can the handle (without dynamic memory) be implemented?
A data-member of a class has to have a concrete type. There is no mechanism to deduce it from an initialiser. What you can do is move the desired factory method into the type.
template<auto FactoryMethod> class Scheme_for;
template<typename TableType, TableType(*FactoryMethod)()>
class Scheme_for<FactoryMethod> : public Scheme_handle<TableType>{
public: Scheme_for(): Scheme_handle<TableType>(FactoryMethod()){}
};
Then you only mention the factory method once per use.
struct MyClass{
Scheme_for<Factory::make_x> x1;
Scheme_for<Factory::make_y> y1;
Scheme_for<Factory::make_y> y2;
Scheme_B x2 = Factory::make_x();
};