I have several classes that each of them has an ID and the Id is passed to the class as a template parameter:
typedef class1<1> baseClass;
typedef class2<2> baseClass;
typedef class<100> baseClass;
Now I need a map so if I can associate 1 with Class1 and 2 with Class2 and so on.
How can I create such vector? I am working on a header only library, so it should be a header only definition.
I am looking something that do the same thing that this code would do (if someone can compile it!):
std::map<int,Type> getMap()
{
std::map<int,Type> output;
output.add(1,class1);
output.add(2,class2);
output.add(100,class100);
}
The idea is that when I get as input 1, I create a class1 and when I receive 2, I create class2.
Any suggestion is very appreciated.
using this data, then I can write a function like this:
void consume(class1 c)
{
// do something interesting with c
}
void consume(class2 c)
{
// do something interesting with c
}
void consume(class3 c)
{
// do something interesting with c
}
void consume(int id,void * buffer)
{
auto map=getMap();
auto data= new map[id](buffer); // assuming that this line create a class based on map, so the map provide the type that it should be created and then this line create that class and pass buffer to it.
consume(data);
}
As a sketch:
class BaseClass { virtual ~BaseClass() = default; };
template<std::size_t I>
class SubClass : public BaseClass {};
namespace detail {
template<std::size_t I>
std::unique_ptr<BaseClass> makeSubClass() { return { new SubClass<I> }; }
template<std::size_t... Is>
std::vector<std::unique_ptr<BaseClass>(*)> makeFactory(std::index_sequence<Is...>)
{ return { makeSubclass<Is>... }; }
}
std::vector<std::unique_ptr<BaseClass>(*)> factory = detail::makeFactory(std::make_index_sequence<100>{});
We populate the vector by expanding a parameter pack, so we don't have to write out all 100 instantiations by hand. This gives you Subclass<0> at factory[0], Subclass<1> at factory[1], etc. up to Subclass<99> at factory[99].
If I understand correctly you want a map to create different types according to a given number.
If that is so, then the code should look something like this:
class Base
{
};
template <int number>
class Type : public Base
{
public:
Type()
{
std::cout << "type is " << number << std::endl;
}
};
using Type1 = Type<1>;
using Type2 = Type<2>;
using Type3 = Type<3>;
using CreateFunction = std::function<Base*()>;
std::map<int, CreateFunction> creators;
int main()
{
creators[1] = []() -> Base* { return new Type1(); };
creators[2] = []() -> Base* { return new Type2(); };
creators[3] = []() -> Base* { return new Type3(); };
std::vector<Base*> vector;
vector.push_back(creators[1]());
vector.push_back(creators[2]());
vector.push_back(creators[3]());
}
output:
type is 1
type is 2
type is 3
If you need only to create object, it would be enough to implement template creator function like:
template<int ID>
Base<ID> Create()
{
return Base<ID>();
}
And then use it:
auto obj1 = Create<1>();
auto obj2 = Create<2>();
// etc
Working example: https://ideone.com/urh7h6
Due to C++ being a statically-typed language, you may choose to either have arbitrary types that do a fixed set of things or have a fixed set of types do arbitrary things, but not both.
Such limitations is embodied by std::function and std::variant. std::function can have arbitrary types call operator() with a fixed signature, and std::variant can have arbitrary functions visit the fixed set of types.
Since you already said the types may be arbitrary, you may only have a fixed set of things you can do with such a type (e.g. consume). The simplest way is to delegate the hard work to std::function
struct Type
{
template<typename T>
Type(T&& t)
: f{[t = std::forward<T>(t)]() mutable { consume(t); }} {}
std::function<void()> f;
};
void consume(Type& t)
{
t.f();
}
What you are looking for is either the Stategy pattern:
#include <iostream>
#include <memory>
#include <string>
#include <vector>
class A {
public:
A() {}
virtual void doIt() {};
};
class Aa : public A {
public:
Aa() {}
virtual void doIt() {
std::cout << "do it the Aa way" << std::endl;
}
};
class Ab : public A {
public:
Ab() {}
virtual void doIt() {
std::cout << "do it the Ab way" << std::endl;
}
};
class Concrete {
public:
Concrete(std::string const& type) {
if (type == ("Aa")) {
_a.reset(new Aa());
} else if (type == "Ab") {
_a.reset(new Ab());
}
}
void doIt () const {
_a->doIt();
}
private:
std::unique_ptr<A> _a;
};
int main() {
std::vector<Concrete> vc;
vc.push_back(Concrete("Aa"));
vc.push_back(Concrete("Ab"));
for (auto const& i : vc) {
i.doIt();
}
return 0;
}
Will output:
do it the Aa way
do it the Ab way
Related
How can one do this, which is obviously impossible C++, in real C++?:
Type decodeUiEnum(UiEnum myEnum) { // impossible: cannot return a data type
// one switch statement to rule them all
switch(myEnum) {
case USER_SELECTED_GREYSCALE: return GreyscalePixel;
case USER_SELECTED_RGB: return RgbPixel;
...
}
}
void doSomeGraphicsMagic1(UiEnum myEnum) {
...
Foo<decodeUiEnum(myEnum)> a(...); // impossible: type not available at compile
// time
...
}
void doSomeGraphicsMagic2(UiEnum myEnum, int blah) {
...
Bar<int, decodeUiEnum(myEnum)> b(...); // impossible
...
}
and the like, so you can just add new types to the top switch statement and not have to modify the other code below it, so long as that code is suitably generic of course? As otherwise, you would need a switch statement within each function to do the necessary type mapping into the templates, which is not as much maintainable code, and lots of duplication. So more generally - if this is approaching it the wrong way, how do we fulfill that intended property of the code?
That is, what I want to do is, in a function taking an enum as parameter, instantiate a template type where the template parameter depends on the enum, without having a switch-on-enum in every function.
Yes it is actually possible.
Trick is based on partial template specification, this approach used by std::get
For example:
#include <iostream>
// specify an enumeration we will use as type index and related data types
enum class UiEnum {
GRAY_SCALE,
RGB_PIXEL
};
struct GreyscalePixel;
struct RgbPixel;
// make base template class
template<UiEnum _EV>
struct ui_enum_type {
};
// do partial type specification trick
// insert typedefs with data type we need for each enumeration value
template<>
struct ui_enum_type<UiEnum::GRAY_SCALE> {
typedef GreyscalePixel pixel_type;
};
template<>
struct ui_enum_type<UiEnum::RGB_PIXEL> {
typedef RgbPixel pixel_type;
};
// demo classes to demonstrate how trick is working at runtime
template<typename T>
struct demo_class {
};
template <>
struct demo_class<GreyscalePixel> {
demo_class()
{
std::cout << "GreyscalePixel" << std::endl;
}
};
template <>
struct demo_class<RgbPixel> {
demo_class()
{
std::cout << "RgbPixel" << std::endl;
}
};
// use swithc trick
static void swich_trick(std::size_t runtimeValue)
{
switch( static_cast<UiEnum>(runtimeValue) ) {
case UiEnum::GRAY_SCALE: {
demo_class< ui_enum_type<UiEnum::GRAY_SCALE>::pixel_type > demo1;
}
break;
case UiEnum::RGB_PIXEL: {
demo_class< ui_enum_type<UiEnum::RGB_PIXEL>::pixel_type > demo2;
}
break;
}
}
int main(int argc, const char** argv)
{
// Do runtime based on the trick, use enum instead of data type
for(std::size_t i=0; i < 2; i++) {
swich_trick(i);
}
return 0;
}
In any case my suggestion - use classic polymorphism instead of template meta-programming over complication. Most modern compilers doing de-virtualization during optimization. For example:
#include <iostream>
#include <memory>
#include <unordered_map>
enum class UiEnum {
GRAY_SCALE,
RGB_PIXEL
};
class GraphicsMagic {
GraphicsMagic(const GraphicsMagic&) = delete;
GraphicsMagic& operator=(const GraphicsMagic&) = delete;
protected:
GraphicsMagic() = default;
public:
virtual ~GraphicsMagic( ) = default;
virtual void doSome() = 0;
};
class GreyscaleGraphicsMagic final: public GraphicsMagic {
public:
GreyscaleGraphicsMagic():
GraphicsMagic()
{
}
virtual void doSome() override
{
std::cout << "GreyscalePixel" << std::endl;
}
};
class RgbGraphicsMagic final: public GraphicsMagic {
public:
RgbGraphicsMagic():
GraphicsMagic()
{
}
virtual void doSome() override
{
std::cout << "RgbPixel" << std::endl;
}
};
int main(int argc, const char** argv)
{
std::unordered_map< UiEnum, std::shared_ptr< GraphicsMagic > > handlers;
handlers.emplace(UiEnum::GRAY_SCALE, new GreyscaleGraphicsMagic() ) ;
handlers.emplace(UiEnum::RGB_PIXEL, new RgbGraphicsMagic() );
for(std::size_t i=0; i < 2; i++) {
handlers.at( static_cast<UiEnum>(i) )->doSome();
}
return 0;
}
You could use std::variant, and then have consuming code std::visit that variant.
First we want a template for "pass a type as a parameter"
template <typename T>
struct tag {
using type = T;
};
Then we define our variant and the factory for it.
using PixelType = std::variant<tag<GreyscalePixel>, tag<RgbPixel>>;
PixelType decodeUiEnum(UiEnum myEnum) {
switch(myEnum) {
case USER_SELECTED_GREYSCALE: return tag<GreyscalePixel>{};
case USER_SELECTED_RGB: return tag<RgbPixel>{};
...
}
}
Now our methods can be written as visitors over PixelType
void doSomeGraphicsMagic1(UiEnum myEnum) {
std::visit([](auto t){
using Pixel = decltype(t)::type;
Foo<Pixel> a(...);
}, decodeUiEnum(myEnum));
}
int doSomeGraphicsMagic2(UiEnum myEnum, int blah) {
return std::visit([blah](auto t){
using Pixel = decltype(t)::type;
Bar<int, Pixel> a(...);
return a.frob();
}, decodeUiEnum(myEnum));
}
I have a base class.
#include <string.h>
class Channel
{
private:
std::string stdstrName;
public:
Channel() : stdstrName("CHANNEL"){ }
Channel(std::string name) : stdstrName(name){ }
void PrintName() { std::cout << stdstrName << std::endl; }
};
which is inherited by Position class.
class PositionChannel : public Channel
{
public:
std::vector<int> keyframes;
PositionChannel() : Channel("POSITION") , keyframes( { 1 , 2, 3 }) { }
};
There is a director class which has the channel clas as its data members.
#include "Channel.h"
#include <memory>
class Director
{
private:
std::vector<std::shared_ptr<Channel>> channels;
public:
void AddChannel(std::shared_ptr<Channel> chn) { channels.push_back(chn); }
void GetChannel(Channel **chn) { *chn = channels[0].get(); }
};
now when in the main function.
// Free function
template<typename T>
void GetChannel(Director *dir)
{
T *chn;
dir->GetChannel(&chn);
}
Director dir;
PositionChannel channel;
std::shared_ptr<Channel> channelPointer = std::make_shared<Channel>(channel);
dir.AddChannel(channelPointer);
GetChannel< PositionChannel>(&dir); // here i get error
this is the error message error C2664: ' cannot convert argument 1 from 'T **' to 'Channel **
if i change the templated function to a non templted function than i do not get any error.
In you GetChannel call, &chn argument is of type PositionChannel**, but the type of Director::GetChannel parameter is Channel**. These two types are not convertible; see, for example this question: Conversion of pointer-to-pointer between derived and base classes?.
I am not sure what are your intentions since the code does not make much sense as is, but you can redefine GetChannel as follows:
template<typename T>
void GetChannel(Director *dir)
{
Channel* ptr;
dir->GetChannel(&ptr);
T *chn = ptr;
}
T can be any type, you can't convert it to a Channel for any type.
There are probably ways to make it work with templates, but I feel like your problem could be more easily solved by using polymorphism with something like this :
void GetChannel(Channel* chn, Director *dir)
{
dir->GetChannel(&chn);
}
And then chn can be any type dervived from Channel.
Yes, Daniel Langr already gave the correct answer. I added a check in the template if the class is derived.
#include <type_traits>
class Channel
{
public:
virtual ~Channel() = default;
};
class PositionChannel : public Channel
{
};
struct Director{
void GetChannel(Channel **c) {}
};
template <typename T,
typename = typename std::enable_if<std::is_base_of<Channel, T>::value, T>::type>
void GetChannel(Director *dir)
{
Channel *chn;
dir->GetChannel(&chn);
T* ptr = static_cast<T*>(chn);
}
int main(void) {
Director dir;
GetChannel<PositionChannel>(&dir);
return 0;
}
I have a set of multiple C++ classes that have the same interface (not derived from each other though). I'm trying to wrap these to make them available in .NET.
I currently have a method that defines the wrapper class using C/C++ #defines and then I can subsequently instantiate classes with a simple line of code
However I can't debug this. Ideally I would like to be able to use a generic or a template. However I can't use a C++ type inside a generic which would be the ultimate way to solve this problem.
Has anyone any idea of how I can do this without using the dreaded macros?
EDIT:
OK Here is an example of the templated class I have written:
template< typename CPPResamplerClass >
ref class TResampler
{
CPPResamplerClass* pResampler;
public:
TResampler( int inputSampleRate, int outputSampleRate, int bufferLen ) :
pResampler( new CPPResamplerClass( inputSampleRate, outputSampleRate, bufferLen ) )
{
}
~TResampler()
{
this->!ResamplerName();
}
!TResampler()
{
if (pResampler)
{
delete pResampler;
pResampler = nullptr;
}
}
property int HistorySize
{
int get()
{
return pResampler->HistorySize();
}
}
array< float >^ ResampleAudio(array< float >^ in)
{
pResampler->Get
array< float >^ out = gcnew array< float >(in->Length);
cli::pin_ptr< float > pIn = &in[0];
cli::pin_ptr< float > pOut = &out[0];
unsigned int inLen = in->Length;
unsigned int outLen = out->Length;
if (pResampler->ResampleAudio(pOut, outLen, pIn, inLen))
{
System::Array::Resize(out, outLen);
return out;
}
return nullptr;
}
};
typedef TResampler< ::Vec::SpeexResample > SpeexResample;
I then want to access this from C# however SpeexResample does not exist. This could well be because I am using a typedef ...
Templates don't exist until they're instantiated. While you could instantiate one explicitly:
template ref class TResampler<SomeNativeClass>;
It wouldn't be exactly user-friendly to use from C#. The exported type will literally have angle brackets in its name. Good luck using that. In C# it's only doable through reflection.
The next best thing is to use derived types. Here's a minimal example:
#include "stdafx.h"
#include <iostream>
namespace CppCli {
class NativeClassA
{
int foo;
public:
NativeClassA(int foo) : foo(foo) { std::cout << "Built native class A" << std::endl; }
int getFoo() const { return foo; }
};
class NativeClassB
{
int foo;
public:
NativeClassB(int foo) : foo(foo) { std::cout << "Built native class B" << std::endl; }
int getFoo() const { return foo; }
};
template<typename NativeClass>
public ref class ManagedWrapper
{
NativeClass* ptr;
public:
ManagedWrapper(int foo)
: ptr(new NativeClass(foo))
{}
~ManagedWrapper()
{
this->!ManagedWrapper();
}
!ManagedWrapper()
{
if (ptr)
{
delete ptr;
ptr = nullptr;
}
}
property int Foo { int get() { return ptr->getFoo(); } }
};
public ref class ManagedWrapperA : ManagedWrapper<NativeClassA>
{
public:
ManagedWrapperA(int foo) : ManagedWrapper(foo) {}
};
public ref class ManagedWrapperB : ManagedWrapper<NativeClassB>
{
public:
ManagedWrapperB(int foo) : ManagedWrapper(foo) {}
};
};
Sure enough, ManagedWrapperA and ManagedWrapperB are visible from C#. Maybe you could macro these definitions and still get a decent debugging experience.
We have
enum Enum {A,B,C,D,E,F,G,H, NumEnums};
class Base {};
template <Enum...> class Thing : public Base {};
and the function
Base* create (std::list<Enum>& input);
is to create an object of the type that corresponds to input. For example,
if input = {A,E,C,G,D};, then the output shall be of type Thing<A,E,C,G,D>* (let's forget the sorting here). Now I know input is obtained during run-time, but by doing a search, the output can be obtained fairly quickly. If Thing had only one parameter (i.e. input has size() one), then the simple
template <int N>
Base* createHelper (const std::list<Enum>& input) {
const Enum En = static_cast<Enum>(N);
if (input.front() == En)
return new Thing<En>;
return createHelper<N+1>(input);
}
template <>
Base* createHelper<NumEnums> (const std::list<Enum>&) {
return nullptr;
}
Base* create (const std::list<Enum>& input) {
return createHelper<0>(input);
}
will do. I tried to generalize the above to any size list (the size would have to be determined during run-time through a similar recursion as above, but that should be fairly quick too). But I got totally lost on how. So I tried to examine the structure of the naïve method:
#include <iostream>
#include <list>
#include <type_traits>
#include <typeinfo>
enum Enum {A,B,C,D,E,F,G,H, NumEnums};
class Base {
public:
virtual void print() const = 0;
};
template <Enum...> class Thing : public Base {
virtual void print() const override {std::cout << typeid(*this).name() << '\n';}
};
Base* create (std::list<Enum>& input) {
if (input.front() == A) {
input.pop_front();
if (input.empty())
return new Thing<A>;
else {
if (input.front() == A) {
input.pop_front();
if (input.empty())
return new Thing<A,A>;
else {
// ....
}
}
else if (input.front() == B) {
input.pop_front();
if (input.empty())
return new Thing<A,B>;
else {
// ....
}
}
}
}
else if (input.front() == B) {
// similar
}
// ...
}
int main() {
std::list<Enum> userInput = {A,B};
// Wish to construct an instance of Thing<A,B> (efficiently).
Base* thing = create(userInput);
thing->print(); // Thing<A,B>
}
I figured I could put this in recursive form. But I cannot think of it. I know the one-dimensional case can be generalized, but I need help here. Or perhaps there is a better way to do it altogether? Once it works, it should not take anymore than a fraction of a second for the create function to return, assuming NumEnums is a decent size and the Thing class has just several template arguments, and not hundreds.
Edit: Turns out, there may be a viable solution here:
Create an associate array between your key and a type factory class.
Dynamically allocate any variables you may need from the type factory once you have it selected (preferably using std::unique_ptr).
The end result may end of looking like this:
std::unordered_map<std::string, type_allocator> str_to_type;
str_to_type["a"] = type_allocator(int); //where type_allocator derives the type of the class from the input variable.
auto variable = str_to_type[input].allocate();
For specific size, if you compute a single index, you may dispatch at runtime to the correct compile time function:
template <std::size_t N>
std::unique_ptr<Base> make_thing3()
{
constexpr Enum a2 = Enum(N % NumEnums);
constexpr Enum a1 = Enum((N / NumEnums) % NumEnums);
constexpr Enum a0 = Enum((N / NumEnums / NumEnums) % NumEnums);
return std::make_unique<Thing<a0, a1, a2>>();
}
template <std::size_t... Is>
std::unique_ptr<Base> make_thing3(std::size_t index, std::index_sequence<Is...>)
{
using maker = std::unique_ptr<Base>();
maker* fs[] = {&make_thing3<Is>...};
return fs[index]();
}
std::unique_ptr<Base> make_thing3(const std::array<Enum, 3u>& a)
{
std::size_t index = 0;
for (Enum e : a) {
index *= NumEnums;
index += e;
}
constexpr std::size_t total = NumEnums * NumEnums * NumEnums;
return make_thing3(index, std::make_index_sequence<total>{});
}
Live Demo
Note: I had to change size of Enum, and reduce my example from make_thing5 to make_thing3 due to compiler limit (not sure if it came from the site or if it is true limits)
This solution shows that though the compile-time is long (due to the many template instantiations), the run-time look-up is instant. The compiler limits is 3 enum values as input though. The empty input case is handled too (the return type being Thing<>*).
#include <iostream>
#include <list>
#define show(variable) std::cout << #variable << " = " << variable << std::endl;
enum Enum {A,B,C,D,E,F,G,H, NumEnums};
class Base {
public:
virtual void print() const = 0;
};
template <Enum... Es> class Thing : public Base {
virtual void print() const override {
const std::list<int> a = {((std::cout << Es << ' '), 0)...};
std::cout << "\nPack size = " << sizeof...(Es) << '\n';
}
};
template <int N, int Size, Enum... Es>
struct Create {
static Base* execute (std::list<Enum>& input) {
const Enum En = static_cast<Enum>(N);
if (input.front() == En) {
input.pop_front();
return Create<0, Size-1, Es..., En>::execute(input);
}
return Create<N+1, Size, Es...>::execute(input);
}
};
template <int N, Enum... Es>
struct Create<N, 0, Es...> {
static Base* execute (std::list<Enum>&) {return new Thing<Es...>;}
};
template <int Size, Enum... Es>
struct Create<NumEnums, Size, Es...> {
static Base* execute (std::list<Enum>&) {return nullptr;} // This will never be reached
};
template <int Size>
Base* do_create (std::list<Enum>& input) {
if (input.size() == Size)
return Create<0, Size>::execute(input);
return do_create<Size+1>(input);
}
template <>
Base* do_create<4> (std::list<Enum>&) {
std::cout << "Cannot exceed 3 values.\n";
return nullptr;
}
Base* create (std::list<Enum>& input) {
return do_create<0>(input);
}
int main() {
std::list<Enum> input = {E,A,F};
Base* thing = create(input);
thing->print(); // 4 0 5
input = {};
create(input)->print(); // Pack size = 0.
}
I've got some legacy code that, instead of virtual functions, uses a kind field to do dynamic dispatch. It looks something like this:
// Base struct shared by all subtypes
// Plain-old data; can't use virtual functions
struct POD
{
int kind;
int GetFoo();
int GetBar();
int GetBaz();
int GetXyzzy();
};
enum Kind { Kind_Derived1, Kind_Derived2, Kind_Derived3 /* , ... */ };
struct Derived1: POD
{
Derived1(): kind(Kind_Derived1) {}
int GetFoo();
int GetBar();
int GetBaz();
int GetXyzzy();
// ... plus other type-specific data and function members ...
};
struct Derived2: POD
{
Derived2(): kind(Kind_Derived2) {}
int GetFoo();
int GetBar();
int GetBaz();
int GetXyzzy();
// ... plus other type-specific data and function members ...
};
struct Derived3: POD
{
Derived3(): kind(Kind_Derived3) {}
int GetFoo();
int GetBar();
int GetBaz();
int GetXyzzy();
// ... plus other type-specific data and function members ...
};
// ... and so on for other derived classes ...
and then the POD class's function members are implemented like this:
int POD::GetFoo()
{
// Call kind-specific function
switch (kind)
{
case Kind_Derived1:
{
Derived1 *pDerived1 = static_cast<Derived1*>(this);
return pDerived1->GetFoo();
}
case Kind_Derived2:
{
Derived2 *pDerived2 = static_cast<Derived2*>(this);
return pDerived2->GetFoo();
}
case Kind_Derived3:
{
Derived3 *pDerived3 = static_cast<Derived3*>(this);
return pDerived3->GetFoo();
}
// ... and so on for other derived classes ...
default:
throw UnknownKindException(kind, "GetFoo");
}
}
POD::GetBar(), POD::GetBaz(), POD::GetXyzzy(), and other members are implemented similarly.
This example is simplified. The actual code has about a dozen different subtypes of POD, and a couple dozen methods. New subtypes of POD and new methods are added pretty frequently, and so every time we do that, we have to update all these switch statements.
The typical way to handle this would be to declare the function members virtual in the POD class, but we can't do that because the objects reside in shared memory. There is a lot of code that depends on these structs being plain-old-data, so even if I could figure out some way to have virtual functions in shared-memory objects, I wouldn't want to do that.
So, I'm looking for suggestions as to the best way to clean this up so that all the knowledge of how to call the subtype methods is centralized in one place, rather than scattered among a couple dozen switch statements in a couple dozen functions.
What occurs to me is that I can create some sort of adapter class that wraps a POD and uses templates to minimize the redundancy. But before I start down that path, I'd like to know how others have dealt with this.
You can use a jump table. This is what most virtual dispatches look like under the hood, and you CAN construct it manually.
template<typename T> int get_derived_foo(POD*ptr) {
return static_cast<T>(ptr)->GetFoo();
}
int (*)(POD*) funcs[] = {
get_derived_foo<Derived1>,
get_derived_foo<Derived2>,
get_derived_foo<Derived3>
};
int POD::GetFoo() {
return funcs[kind](this);
}
For a short example.
What exactly are the limitations of being in shared memory? I realized that I don't know enough here. Does it mean that I can't use pointers, because someone in another process will be trying to use those pointers?
You could use a string map, where each process gets it's own copy of the map. You'd have to pass this in to GetFoo() so that it can find it.
struct POD {
int GetFoo(std::map<int, std::function<int()>& ref) {
return ref[kind]();
}
};
Edit: Of course, you don't have to use a string here, you could use an int. I just used it as example. I should change it back. Infact, this solution is pretty flexible, but the important thing is, make a copy of process-specific data, e.g. function pointers or whatever, and then pass it in.
Here's the template-metaprogramming path I'm going down now. Here is what I like about it:
Adding support for a new kind only requires updating LAST_KIND and adding a new KindTraits.
There is a simple pattern for adding a new function.
Functions can be specialized for particular kinds if necessary.
I can expect compile-time errors and warnings, rather than mysterious run-time misbehavior, if I screw anything up.
There are a couple of concerns:
POD's implementation is now dependent upon the interfaces of all the derived classes. (This is already true in the existing implementation, so I'm not worried about it, but it is a bit of a smell.)
I'm counting on the compiler to be smart enough to generate code that is roughly equivalent to the switch-based code.
Many C++ programmers will scratch their heads upon seeing this.
Here's the code:
// Declare first and last kinds
const int FIRST_KIND = Kind_Derived1;
const int LAST_KIND = Kind_Derived3;
// Provide a compile-time mapping from a kind code to a subtype
template <int KIND>
struct KindTraits
{
typedef void Subtype;
};
template <> KindTraits<Kind_Derived1> { typedef Derived1 Subtype; };
template <> KindTraits<Kind_Derived2> { typedef Derived2 Subtype; };
template <> KindTraits<Kind_Derived3> { typedef Derived3 Subtype; };
// If kind matches, then do the appropriate typecast and return result;
// otherwise, try the next kind.
template <int KIND>
int GetFooForKind(POD *pod)
{
if (pod->kind == KIND)
return static_cast<KindTraits<KIND>::Subtype>(pod)->GetFoo();
else
return GetFooForKind<KIND + 1>(); // try the next kind
}
// Specialization for LAST_KIND+1
template <> int GetFooForKind<LAST_KIND + 1>(POD *pod)
{
// kind didn't match anything in FIRST_KIND..LAST_KIND
throw UnknownKindException(kind, "GetFoo");
}
// Now POD's function members can be implemented like this:
int POD::GetFoo()
{
return GetFooForKind<FIRST_KIND>(this);
}
You can experiment with Curiously recurring template pattern. It's a bit complicated, but when you cannot use pure virtual functions it can be helpful.
Here is an approach that uses virtual methods to implement the jump table, without requiring the Pod class or the derived classes to actually have virtual functions.
The objective is to simplify adding and removing methods across many classes.
To add a method, it needs to be added to Pod using a clear and common pattern, a pure virtual function needs to be added to PodInterface, and a forwarding function must be added to PodFuncs using a clear and common pattern.
Derived classes need only have a file static initialisation object to set things up, otherwise look pretty much like they already do.
// Pod header
#include <boost/shared_ptr.hpp>
enum Kind { Kind_Derived1, Kind_Derived2, Kind_Derived3 /* , ... */ };
struct Pod
{
int kind;
int GetFoo();
int GetBar();
int GetBaz();
};
struct PodInterface
{
virtual ~PodInterface();
virtual int GetFoo(Pod* p) const = 0;
virtual int GetBar(Pod* p) const = 0;
virtual int GetBaz(Pod* p) const = 0;
static void
do_init(
boost::shared_ptr<PodInterface const> const& p,
int kind);
};
template<class T> struct PodFuncs : public PodInterface
{
struct Init
{
Init(int kind)
{
boost::shared_ptr<PodInterface> t(new PodFuncs);
PodInterface::do_init(t, kind);
}
};
~PodFuncs() { }
int GetFoo(Pod* p) const { return static_cast<T*>(p)->GetFoo(); }
int GetBar(Pod* p) const { return static_cast<T*>(p)->GetBar(); }
int GetBaz(Pod* p) const { return static_cast<T*>(p)->GetBaz(); }
};
//
// Pod Implementation
//
#include <map>
typedef std::map<int, boost::shared_ptr<PodInterface const> > FuncMap;
static FuncMap& get_funcmap()
{
// Replace with other approach for static initialisation order as appropriate.
static FuncMap s_funcmap;
return s_funcmap;
}
//
// struct Pod methods
//
int Pod::GetFoo()
{
return get_funcmap()[kind]->GetFoo(this);
}
//
// struct PodInterface methods, in same file as s_funcs
//
PodInterface::~PodInterface()
{
}
void
PodInterface::do_init(
boost::shared_ptr<PodInterface const> const& p,
int kind)
{
// Could do checking for duplicates here.
get_funcmap()[kind] = p;
}
//
// Derived1
//
struct Derived1 : Pod
{
Derived1() { kind = Kind_Derived1; }
int GetFoo();
int GetBar();
int GetBaz();
// Whatever else.
};
//
// Derived1 implementation
//
static const PodFuncs<Derived1>::Init s_interface_init(Kind_Derived1);
int Derived1::GetFoo() { /* Implement */ }
int Derived1::GetBar() { /* Implement */ }
int Derived1::GetBaz() { /* Implement */ }
Here is an example using Curiously recurring template pattern. This may suit your needs if you know more info at the compile time.
template<class DerivedType>
struct POD
{
int GetFoo()
{
return static_cast<DerivedType*>(this)->GetFoo();
}
int GetBar()
{
return static_cast<DerivedType*>(this).GetBar();
}
int GetBaz()
{
return static_cast<DerivedType*>(this).GetBaz();
}
int GetXyzzy()
{
return static_cast<DerivedType*>(this).GetXyzzy();
}
};
struct Derived1 : public POD<Derived1>
{
int GetFoo()
{
return 1;
}
//define all implementations
};
struct Derived2 : public POD<Derived2>
{
//define all implementations
};
int main()
{
Derived1 d1;
cout << d1.GetFoo() << endl;
POD<Derived1> *p = new Derived1;
cout << p->GetFoo() << endl;
return 0;
}
Expanding on the solution you ended up with, the following solves the mapping to derived functions at program initialization:
#include <typeinfo>
#include <iostream>
#include <functional>
#include <vector>
enum Kind
{
Kind_First,
Kind_Derived1 = Kind_First,
Kind_Derived2,
Kind_Total
};
struct POD
{
size_t kind;
int GetFoo();
int GetBar();
};
struct VTable
{
std::function<int(POD*)> GetFoo;
std::function<int(POD*)> GetBar;
};
template<int KIND>
struct KindTraits
{
typedef POD KindType;
};
template<int KIND>
void InitRegistry(std::vector<VTable> &t)
{
typedef typename KindTraits<KIND>::KindType KindType;
size_t i = KIND;
t[i].GetFoo = [](POD *p) -> int {
return static_cast<KindType*>(p)->GetFoo();
};
t[i].GetBar = [](POD *p) -> int {
return static_cast<KindType*>(p)->GetBar();
};
InitRegistry<KIND+1>(t);
}
template<>
void InitRegistry<Kind_Total>(std::vector<VTable> &t)
{
}
struct Registry
{
std::vector<VTable> table;
Registry()
{
table.resize(Kind_Total);
InitRegistry<Kind_First>(table);
}
};
Registry reg;
int POD::GetFoo() { return reg.table[kind].GetFoo(this); }
int POD::GetBar() { return reg.table[kind].GetBar(this); }
struct Derived1 : POD
{
Derived1() { kind = Kind_Derived1; }
int GetFoo() { return 0; }
int GetBar() { return 1; }
};
template<> struct KindTraits<Kind_Derived1> { typedef Derived1 KindType; };
struct Derived2 : POD
{
Derived2() { kind = Kind_Derived2; }
int GetFoo() { return 2; }
int GetBar() { return 3; }
};
template<> struct KindTraits<Kind_Derived2> { typedef Derived2 KindType; };
int main()
{
Derived1 d1;
Derived2 d2;
POD *p;
p = static_cast<POD*>(&d1);
std::cout << p->GetFoo() << '\n';
p = static_cast<POD*>(&d2);
std::cout << p->GetBar() << '\n';
}