Sign in to follow this  
aclysma

[C++] Template argument restrictions

Recommended Posts

I am trying to make a class that registers up to one instance of any single object. More concretely, I want my WidgetRegister class to implement an api like this:
struct WidgetRegister
{
  template<class T>
  void registerWidget(T *widget);

  template<class T>
  T *getWidget();

  template<class T>
  void unregisterWidget();
}
Now, I know that I can't convert T to a string through simple language support, so I plan to make a base Widget subclass like this:
struct Widget
{
  static std::string getName() { return "baseWidget"; }
}
There are a few problems I'm facing though: 1) What is the best way to ensure that T can only be a Widget or subclass of WidgetRegister? I am finding now that if I pass an int or some other object, calling T::getName() will fail as long as it doesn't implement getName(), but will have an arguably non-intuitive error. But even worse, if it does have getName(), the template function will move ahead with whatever getName() returned even though T doesn't inherit from Widget. I guess I could mangle getName() to something longer and more specific to prevent accidents but this doesn't feel "right." I think the best solution would be some construct in the declaration like template<class T : public Widget> ... Is there a language construct like this that I can use? 2) Another nasty: while getWidget() and unregisterWidget() can't deduce T and require <MyWidget> explicitly stated, registerWidget *can* deduce T. If I do:
  WidgetExtended *widgetExtended = new WidgetExtended();
  Widget *base = widgetExtended;
  addGameComponent(base);
registerWidget(T *) will use Widget::getName() instead of WidgetExtended::getName() and thus be WRONG! Can I enforce that the user call the function with <>s? I can completely sidestep this issue by using virtual functions but now I have to have a T* instantiated to get the name. This will work fine for the register() but not at all for the get and remove. 3) If I continue using the static T::getName() function, is there a way I can enforce subclasses to implement a static T::getName() function also similar in effect to pure virtual functions? i.e.
class MyWidget : Widget
{
  //COMPILE ERROR: because getName() not defined
}
Here the code would not compile if MyWidget was registered with the WidgetRegister but would give a non-intuitive error. Worse, if WidgetRegister and MyWidget never interact, the code WOULD compile. So, I'm not sure if I should even go this route. I NEED to dynamically instantiate as many widgets as I want, and I NEED to be able to refer to them by string name. I do NOT want typecasting anywhere (I'm going for a completely type safe solution if possible). I think this almost works, but it seems like too much trouble for what overall is kind of a hack. While the outside interface is fairly simple, elegant, and very type safe, the internals are more complicated than I would like. Even if all the problems above were solved, the code would still be non-trivial to maintain. Am I trying too hard? Is there a better way?

Share this post


Link to post
Share on other sites
For 1 and 2, the easiest way to make certain that the T * is a widget is to assign it to a Widget pointer, inside the function. Ex:

template<class T>
void registerWidget(T *widget) {
Widget * make_sure_im_a_widget = widget;
// stuff
}

However, you can instead use:

template<class T>
void registerWidget(Widget * widget) {
// stuff
}

Now, it can no longer deduce the template from the arguments, so the user is forced to use a explicit instantiation and the widget needs to be derived from Widget.

However, what you seem to be doing is implementing your own RTTI facilities. This is pretty silly since C++ contains perfectly workable RTTI. An example of what you seem to be trying to do, using RTTI instead:

#include <typeinfo>
#include <map>
#include <iostream>

class TypeInfoProxy {
public:
TypeInfoProxy(const std::type_info * t) : ti(t) {}
const std::type_info * ti;
};

inline bool operator<(const TypeInfoProxy & lhs, const TypeInfoProxy & rhs) {
return lhs.ti->before(*rhs.ti) != 0;
}

inline bool operator>(const TypeInfoProxy & lhs, const TypeInfoProxy & rhs) {
return rhs.ti->before(*lhs.ti) != 0;
}

inline bool operator<=(const TypeInfoProxy & lhs, const TypeInfoProxy & rhs) {
return !(rhs > lhs);
}

inline bool operator>=(const TypeInfoProxy & lhs, const TypeInfoProxy & rhs) {
return !(rhs < lhs);
}

inline bool operator==(const TypeInfoProxy & lhs, const TypeInfoProxy & rhs) {
return ((*lhs.ti) == (*rhs.ti)) != 0;
}

inline bool operator!=(const TypeInfoProxy & lhs, const TypeInfoProxy & rhs) {
return ((*lhs.ti) != (*rhs.ti)) != 0;
}

struct Widget {
virtual ~Widget() {}
virtual void print_me(void) = 0;
};

struct WidgetRegistrar {
void register_widget(Widget * widget) {
widget_map[&typeid(*widget)] = widget;
}
template <typename T>
T * get_widget(void) {
return &dynamic_cast<T &>(*widget_map[&typeid(T)]);
}
template <typename T>
void unregister_widget(void) {
widget_map.erase(&typeid(T));
}

typedef std::map<TypeInfoProxy, Widget *> WidgetMap;
WidgetMap widget_map;
};

struct A : Widget {
void print_me(void) { std::cout << "I'm an A!" << std::endl; }
};
struct B : Widget {
void print_me(void) { std::cout << "I'm a B!" << std::endl; }
};

int main(int, char **) {
WidgetRegistrar registrar;
A a;
B b;
registrar.register_widget(&a);
registrar.register_widget(&b);
A * pa = registrar.get_widget<A>();
pa->print_me();
B * pb = registrar.get_widget<B>();
pb->print_me();
registrar.unregister_widget<A>();
registrar.unregister_widget<B>();

std::cout << registrar.widget_map.size();

return 0;
}

Share this post


Link to post
Share on other sites
Quote:
Original post by aclysma
I think the best solution would be some construct in the declaration like
template<class T : public Widget> ...

Is there a language construct like this that I can use?

Not yet. The next C++ standard, C++0x, will feature "concepts", which are basically restrictions on template arguments. With concepts, you can specify that a particular template must be derived from a particular base class.

Share this post


Link to post
Share on other sites
Why are you even using templates here? Why not just pass a widget pointer:

struct WidgetRegister
{
void registerWidget(Widget *widget);
void registerWidget(WidgetRegister *widget);

Widget *getWidget();

void unregisterWidget();

}

Share this post


Link to post
Share on other sites
1)
template<typename T> 
typename boost::enable_if< boost::is_base_of< Widget , T > >::type
foo(T* widget);
3) This isn't probably isn't worth the hassle and requires fairly significant and intrusive changes but here you go.


// Warning: Untested, the basic idea should work but theres probably some
// bugs that need sorting out.

// A small utility
template<typename T>
struct dummy { };

namespace sfinae
{
typedef char one;
typedef struct { char v[2]; } two;
};

// Use sfinae to check for a static T::getName with the correct signature
template<typename T>
struct has_getName
{
template<std::string (*)(dummy<T>)>
struct wrapper { };

template<typename T>
static sfinae::one test(wrapper<*T::getName>*);

template<typename T>
static sfinae::two test(...);

enum { value = sizeof(test<T>(0)) == sizeof(sfinae::one) };
};

// widget has to be a CRTP class to perform the test.
template<typename Derived>
struct widget
{
// Check that Derived has a getName member with the right signature
BOOST_MPL_ASSERT_MSG((has_getName<Derived>::value), WIDGET_MUST_OVERRIDE_getName, (types<Base>));

// For a given widget Derived getName must have signature
// std::string (dummy<Derived>) so that we can distinguish the getName
// in derived from the one in the base class. We give it a default value
// so that the caller doesn't have to know about it.
static std::string getName(dummy<widget> = dummy<widget>()) { return "widget"; }
};

// extends<Derived, Base> is used to inherit from a widget other then widget<Derived>
// This is the simplest possible implimentation and assumes widget<T>
// does nothing but the getName check, it's possible to write extends so
// this isn't the case and so that it avoids the multiple inheritance
// but thats left as an exercise for the reader.
template<typename Derived, Base>
struct extends : Base , widget<Derived>
{ };

// Works
struct my_widget : widget<my_widget>
{
static std::string getName(dummy<my_widget> = dummy<my_widget>())
{ return "my_widget"; }
};

// Error: WIDGET_MUST_OVERRIDE_getName with T0 = error_widget
struct error_widget : widget<error_widget>
{
};

// Works
struct my_derived_widget : extends< my_derived_widget , my_widget >
{
static std::string getName(dummy<my_derived_widget> = dummy<my_derived_widget>())
{ return "my_derived_widget"; }
};

// Error: WIDGET_MUST_OVERRIDE_getName with T0 = my_derived_error_widget
struct my_derived_error_widget : extends< my_derived_error_widget , my_widget >
{
};



Personally I'd try and avoid the whole getName thing using something like this:
struct WidgetRegister
{
WidgetRegister() : widget_register_id(next_widget_register_id)
{ ++next_widget_register_id; }

template<class T>
void registerWidget(T *widget) { storage<T>::values[widget_register_id] = widget; }

template<class T>
T* getWidget() { return storage<T>::values[widget_register_id]; };

template<class T>
void unregisterWidget() { storage<T>::values.erase(widget_register_id); }

private:
static std::size_t next_widget_register_id;
std::size_t widget_register_id;

template<typename T>
struct storage
{
static std::map<std::size_t, T*> values;
};
};

template<typename T>
std::map<std::size_t, T*> WidgetRegister::storage<T>::values;

std::size_t WidgetRegister::next_widget_register_id;

Share this post


Link to post
Share on other sites
Wow, some great responses here. Thanks!

SiCrane: Thanks for the info on RTTI and pointing out a way to enforce explicit template argument specification.

ToohrVyk/Julian90: SFINAE is totally new to me and I definately learned something today. Pretty cool trick!

King Mir: While your method is certainly straightforward, it is not typesafe as it requires the user of the register class to upcast, which is exactly what I'm trying to avoid.

For now, I think I'm going to go with SiCrane's approach as the cost of using RTTI looks very minimal (small increase in executable size). Using RTTI, I will no longer need to implement any getName() functions as RTTI will provide this. Not needing a getName() function solves 1, 2, and 3.

Should RTTI not work for me (either the cost is unacceptable or more likely the VC++8 compiler's type name is unpredictable) you can bet I'll be back to this post to dig up a SFINAE approach which while complex, should also solve this problem.

Thanks again for the excellent responses and in particular SiCrane and Julian for writing up such detailed responses.

Share this post


Link to post
Share on other sites
In MSVC, if you have exception handling enabled (which you should if you use any standard library classes), then the additional overhead of RTTI is very minimal, since the exception handling functionality in MSVC uses RTTI data structures.

Share this post


Link to post
Share on other sites
Quote:
Original post by aclysma
King Mir: While your method is certainly straightforward, it is not typesafe as it requires the user of the register class to upcast, which is exactly what I'm trying to avoid.
Only with getWidget(), so there is no reason to make the other methods templates. Furthermore, using a template here does not eliminate the casting, it just moves it inside the getWidget function. The same problems of ensuring the correct type of widget is used still exist.

Share this post


Link to post
Share on other sites
Quote:
Original post by King Mir
Only with getWidget(), so there is no reason to make the other methods templates. Furthermore, using a template here does not eliminate the casting, it just moves it inside the getWidget function. The same problems of ensuring the correct type of widget is used still exist.


Good point about the templates being unnecessary for all functions but the get. (assuming typeid() works on variables as well as the type name itself, and that it always returns the most extended class). I will look into this.

As to typesafety of the get, you're exactly right - the cast is still there and I'm just hiding it. But I'm banking on the fact that T::getName() or the RTTI type_info::name() function returns the same thing all the time and is unique per class. If this is the case, I believe the upcast is provably safe because the only way for the widget to be found would be to have registered in the map with what T::getName() or type_info::name() returned.

Share this post


Link to post
Share on other sites
Why are you still using the name to index your types? std::type_info::name() will return the same value for each call, but is not guaranteed to be unique for all types. However, the std::type_info objects are guaranteed to be unique for all types, so can be used as an index like in the code sample I posted earlier.

In any case, typeid will return the dynamic type of an object even if accessed through a base class pointer so long as that base class is legitimate polymorphic base (i.e. has virtual functions).

Share this post


Link to post
Share on other sites
I'm using the name because I intend in the future to support serialization into/out of xml in a human-readable way. In addition, I want to make the widgets implementable by script or C++ and allow the widgets to get to each other by name. Strings are readable and intuitive, and I feel that they meet both of these needs.

I guess a way to solve this would be to have a string-to-type_info map, but I haven't gotten that far yet. Hopefully by the end of this week I'll be far enough along in my prototype to have a better defined solution.

Just FYI to anyone who wants to know why I'm trying to do this, I'm intending to use this facility to implement the component system that was used in these slides: http://www.drizzle.com/~scottb/gdc/game-objects_files/frame.htm. (sorry don't know how to post links in these forums). I'm building a prototype now that is working (even with using type_info::name()).

It seems that VC8 returns either "class MyClass" or "struct MyStruct," so it is unique enough for my use now but I'd like to find a better way obviously that is non-compiler specific. The lookup table might work well for this.

Thanks again for the great posts. I'll continue checking this post for more suggestions.

Share this post


Link to post
Share on other sites
For MSVC, AFAIK, there's only one situation where two different types will have the same type_info.name(): if they're both defined with the same name in different anonymous namespaces. If you avoid anonymous namespaces, you shouldn't have name() collisions with MSVC. You can also use the MSVC extension raw_name() for a string that, AFAIK, won't be duplicated at all. However, raw_name() isn't really in a format for human consumption. (Unless you consider strings like ".?AUA@?A0xb3a386fb@@" to be suitable for human consumption.)

Share this post


Link to post
Share on other sites
I'm confused as to what your getWidget function actually does. Does it need to know the type of the widget to look it up/instantiate it, or does it just need to ensure the return is correct? Cause if the latter, hiding your cast in a template isn't going to do a thing for you.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this