Template magic required

Started by
2 comments, last by Harry Hunt 15 years, 10 months ago
Hi! I'm battling with the C++ type system, and it looks like I'm losing. So I have the following struct:

template <typename T, typename U>
struct Accessor
{
    void (T::*setter)(const U value);
    U (T::*getter)() const;
};

T is guaranteed to be an instance of a base class "Base", but the methods the two function pointers point to don't have to be implemented in "Base" but can be implemented in a derived class. The problem I'm having is that I need to create a list of Accessors:

Accessor<Derived, int>* accessor1 = new Accessor<Derived, int>;
accessor1->setter = &Derived::SetValue;
accessor1->getter = &Derived::GetValue;

Accessor<Another, bool>* accessor2 = new Accessor<Another, bool>;
accessor2->setter = &Another::SetAnother;
accessor2->getter = &Another::GetAnother;

vector<void*> accessors;
accessors.push_back(accessor1);
accessors.push_back(accessor2);

Apart from the fact that void* is the devil, the problem with the code above is that if I ever want to take something out of that list and use it, I need to know both T and U. The thing is, that I only know U. In other words, if I were to write a function like this:

template <typename U>
void SetValue(void* accessor, Base* obj, const U value)
{
    Accessor<???, U>* a = reinterpret_cast< Accessor<???, U>* >(accessor);
    obj->*(a->setter)(value);
}

I would have to know what the "???" is (but I don't). Am I out of luck, or is there a way of doing that? Thanks a bunch in advance P.S.: If you have to ask: it's actually part of a meta-class system for my scene graph.
Advertisement
I don't have a ready-made solution for you, but you can try looking at "multiple dispatch" for ideas. Google keywords: multiple dispatch, double dispatch, multi methods. It's a technique for dynamically selecting a method based on the type of two objects, and it's related to your problem.
template < class T >struct Accessor {  boost::function< void(T) > setter;  boost::function< T() > getter;};


Use boost::bind to provide the setter/getter pointers.

There's another way I was using that combines templated free functions, but I'll need to look it up. It doesn't really bring any major advantages though, since it's part of what boost::bind does anyway.

Also, this may not be the best way for computationally expensive parts of code, since it requires a virtual call.


Edit:
The other way is to use templates to instantiate multiple function proxies:
template < class T, class P1 >void setter(void * ptr, P1 p1){  ((T *)ptr)->setter(p1);};...// create function pointerfp = setter<MyClass, int>;
This gains you a fixed function signature of void setter(void*,P1). The disadvantage is that you need to hard-code the name of '->setter'. Writing the proxy to this should be straight-forward.

MVC does wonders with this approach though, resolving all references at compile time, inlining everything possible. GCC remains conservative.


Next approach that also avoid virtual functions involves passing function pointer as template parameter:
template < class T, class P1, void (T::*fp)(P1) >void setter(void * ptr, P1 p1){  T * ptr = (T *)ptr;  (ptr->*fp)(p1);};...fp = getter<MyClass, int, &MyClass::Setter>


Here you also get fixed function signature, and you don't hard-code the function name.

The disasvantage of this is the need to specify function pointer as template parameter. This causes a few problems when trying to construct the accessor, since you need to initialize it in constructor - you need to pass fully qualified setter (or setter proxy) pointer, which involves fully specializing the template with member function pointer.

Unfortunately, it turns out there's some syntactic issues when trying to do so, so it may require a copy constructor, or something like bind. In addition, you can no longer change the function pointer once constructed, and you need to specify it within template, not through assignment.

In addition, when constructing the function pointer, you need to specify all 3 parameters, the class, the type and the Class::Function member pointer, as well as the pointer to instance. This may not seem like a big deal, but it really is a lot of messy syntax.

While not a problem as such, the syntax gets ugly quick. Same as above, virtual functions are resolved properly, MVC is good at optimization, GCC conservative.

If all you need this for is single-parameter accessors, then the last approach, after you get through all the hoops with compiler will likely be optimal (much faster, and considerably more light-weight than boost::function), but it will almost certainly require you to use macros to register the function pointers.

[Edited by - Antheus on June 14, 2008 6:32:36 PM]
Thank you so much!
I'm not using boost in that specific project so I went for the last solution you suggested and it works like a charm! I had no idea that you could use member function pointers as template arguments, but this actually opens a ton of new possibilities.

So thanks again! You're a real life saver. I wish I could give you a higher rating, but it looks like I already decided that you're "extremely helpful" on a previous occasion.

This topic is closed to new replies.

Advertisement