You can create a init-time function table, such that each member of this table corresponds to one of your case statements above. Then setSize
can use this table instead. By using constexpr
min and max parameters, you can specify the bounds of this table with template specialisation, and populate it with template instantiations of 'maker' functions that create your Foo objects.
Here is the declaration of the function table code (all in the private section of FooWrapper):
template<unsigned i>
static std::unique_ptr<IFoo> fooMaker()
{
return std::make_unique< Foo<i> >();
}
static constexpr unsigned FOO_MIN = 1;
static constexpr unsigned FOO_MAX = 3;
using FooMakerFn = std::unique_ptr<IFoo>();
template<unsigned min>
static std::array<FooMakerFn*, FOO_MAX-FOO_MIN-1>& initFooFnTable(std::array<FooMakerFn*, FOO_MAX-FOO_MIN-1>& fnTable);
static std::array<FooMakerFn*, FOO_MAX-FOO_MIN-1> fooFnTable;
Here is the definition of the function table creation, including specialisation for terminating case:
std::array<FooWrapper::FooMakerFn*, FooWrapper::FOO_MAX-FooWrapper::FOO_MIN-1> FooWrapper::fooFnTable = FooWrapper::initFooFnTable<FooWrapper::FOO_MIN>(FooWrapper::fooFnTable);
template<unsigned min>
std::array<FooWrapper::FooMakerFn*, FooWrapper::FOO_MAX-FooWrapper::FOO_MIN-1>& FooWrapper::initFooFnTable(std::array<FooWrapper::FooMakerFn*, FooWrapper::FOO_MAX-FOO_MIN-1>& fnTable)
{
fnTable[min - FOO_MIN] = FooWrapper::fooMaker<min>;
return initFooFnTable<min+1>(fnTable);
}
template<>
std::array<FooWrapper::FooMakerFn*, FooWrapper::FOO_MAX-FooWrapper::FOO_MIN-1>& FooWrapper::initFooFnTable<FooWrapper::FOO_MAX>(std::array<FooWrapper::FooMakerFn*, FooWrapper::FOO_MAX-FooWrapper::FOO_MIN-1>& fnTable)
{
fnTable[FOO_MAX - FOO_MIN] = FooWrapper::fooMaker<FooWrapper::FOO_MAX>;
return fnTable;
}
And here is the complete code:
#include <memory>
#include <string>
#include <iostream>
/* Interface
*/
struct IFoo
{
virtual void lol(void) = 0;
};
/* Template Specialization
*/
template<std::size_t N>
class Foo : public IFoo
{
void lol(void)
{
std::cout << "Lol: " << N << std::endl;
}
};
/* Wrapper for the template
*/
class FooWrapper : public IFoo
{
std::unique_ptr<IFoo> mfoo;
public:
void setSize(std::size_t size)
{
if(size >= FOO_MIN && size <= FOO_MAX)
mfoo = fooFnTable[size - FOO_MIN]();
else
throw std::runtime_error(std::to_string(size) + " not supported.");
}
FooWrapper(std::size_t size)
{
this->setSize(size);
}
void lol(void)
{
mfoo->lol();
}
private:
template<unsigned i>
static std::unique_ptr<IFoo> fooMaker()
{
return std::make_unique< Foo<i> >();
}
static constexpr unsigned FOO_MIN = 1;
static constexpr unsigned FOO_MAX = 3;
using FooMakerFn = std::unique_ptr<IFoo>();
template<unsigned min>
static std::array<FooMakerFn*, FOO_MAX-FOO_MIN-1>& initFooFnTable(std::array<FooMakerFn*, FOO_MAX-FOO_MIN-1>& fnTable);
static std::array<FooMakerFn*, FOO_MAX-FOO_MIN-1> fooFnTable;
};
std::array<FooWrapper::FooMakerFn*, FooWrapper::FOO_MAX-FooWrapper::FOO_MIN-1> FooWrapper::fooFnTable = FooWrapper::initFooFnTable<FooWrapper::FOO_MIN>(FooWrapper::fooFnTable);
template<unsigned min>
std::array<FooWrapper::FooMakerFn*, FooWrapper::FOO_MAX-FooWrapper::FOO_MIN-1>& FooWrapper::initFooFnTable(std::array<FooWrapper::FooMakerFn*, FooWrapper::FOO_MAX-FOO_MIN-1>& fnTable)
{
fnTable[min - FOO_MIN] = FooWrapper::fooMaker<min>;
return initFooFnTable<min+1>(fnTable);
}
template<>
std::array<FooWrapper::FooMakerFn*, FooWrapper::FOO_MAX-FooWrapper::FOO_MIN-1>& FooWrapper::initFooFnTable<FooWrapper::FOO_MAX>(std::array<FooWrapper::FooMakerFn*, FooWrapper::FOO_MAX-FooWrapper::FOO_MIN-1>& fnTable)
{
fnTable[FOO_MAX - FOO_MIN] = FooWrapper::fooMaker<FooWrapper::FOO_MAX>;
return fnTable;
}
int main(void)
{
FooWrapper a(3u); // ok
a.setSize(2u); // ok
a.setSize(0u); // will throw an exception at runtime
a.lol();
return EXIT_SUCCESS;
}