CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Results 1 to 11 of 11
  1. #1
    Join Date
    Apr 2000
    Location
    Belgium (Europe)
    Posts
    4,626

    change behaviour of a template class depending on the type ?

    is there a way to have a template class respond to missing stuff in a template type ?

    Code:
    template <typename Type>
    class MyClass
    {
    public:
              enum { ID = Type::ID }; // revert to 1 if  Type::ID doesn't exist.
    };
    If the Type passed to the template has an ID member (required to be an enum or a static const int), use it, if it is missing revert to a default value.

    I'm kind of hoping I can use this as a simplified way of configuring how MyClass works, without requiring Type to explicitely needing to define what it doesn't care about.

    It needs to be resolved at compiletime, as it determines the number of elements in member array variables.

  2. #2
    Join Date
    Jun 2009
    Location
    France
    Posts
    2,513

    Re: change behaviour of a template class depending on the type ?

    To give you a quick answer: Yes, but it is complicated. You want to search for either "traits" (general solution), or, in your particular case: SFINAE.
    Is your question related to IO?
    Read this C++ FAQ article at parashift by Marshall Cline. In particular points 1-6.
    It will explain how to correctly deal with IO, how to validate input, and why you shouldn't count on "while(!in.eof())". And it always makes for excellent reading.

  3. #3
    Join Date
    Oct 2008
    Posts
    1,456

    Re: change behaviour of a template class depending on the type ?

    you can exploit SFINAE and overload resolution ( or partial template specialization ), something like

    Code:
    namespace details
    {
    
    template <class T,bool>
    struct GetId_ { enum { ID = 1 }; };
    
    template <class T>
    struct GetId_ <T,true> { enum { ID = T::ID }; };
    
    template <int>
    struct SelectOverload_ { typedef bool Bool; };
    
    template <class T>
    bool HasId_( typename SelectOverload_<T::ID>::Bool );
    
    template <class T>
    bool* HasId_( char );
    
    }
    
    template <class T>
    struct GetId
    {
    	enum { ID = details::GetId_< T, sizeof( details::HasId_<T>(true) ) == sizeof( true ) >::ID };
    };
    
    struct A { enum { ID = 3 }; };
    struct B { };
    
    int id1 = GetId<A>::ID; // == 3
    int id2 = GetId<B>::ID; // == 1
    BTW, if you used an std::integral_constant ( or something similar, that is a type ) instead of an enum you could use boost MPL to sort these kind of issues ( and much more ) ...

  4. #4
    Join Date
    Apr 2000
    Location
    Belgium (Europe)
    Posts
    4,626

    Re: change behaviour of a template class depending on the type ?

    Quote Originally Posted by monarch_dodra View Post
    To give you a quick answer: Yes, but it is complicated. You want to search for either "traits" (general solution), or, in your particular case: SFINAE.
    mkay... if I understand the "traits" thing. This means you would need to derive the class from a generic (default) traits class and you "overload" the stuff in the traits that don't match the default in the traits ?
    Code:
    class Traits
    {
    public:
    	enum { ID = 1 }; // define default for ID
    };
    template <typename T>
    class Container
    {
    public:
    	enum { ID = T::ID }; // use the ID from T, or from it's base type if it's not in T
    };
    class A : public Traits
    {
    public:
    	enum { ID = 14 }; // "overload" the default from Traits
    	int a;
    };
    class B : public Traits
    {
    public:
    	// no ID, use default.
    	int b;
    };
    with
    Code:
    int aid = Container<A>::ID;  // 14 == OK
    int bid = Container<B>::ID;  // 1 == OK
    Not sure why you call this complicated This at least isn't complex at all, it's quite simple and elegant. Unfortunately this solution doesn't work for me. Deriving from a base class isn't an option as the A and B classes need to remain aggregate.
    Code:
    A a[] = 
    {
    	{ 1, },
    	{ 2, },
    };
    with the traits above results in :
    error C2552: 'a' : non-aggregates cannot be initialized with initializer list
    'A' : Types with a base are not aggregate

    Or did you mean something else with traits ?


    the SFINAE... need to look into that.... (see below)

  5. #5
    Join Date
    Apr 2000
    Location
    Belgium (Europe)
    Posts
    4,626

    Re: change behaviour of a template class depending on the type ?

    Quote Originally Posted by superbonzo View Post
    you can exploit SFINAE and overload resolution ( or partial template specialization ), something like
    Ok... my head is spinning around now. I'm not getting how this works (yet).
    this'll take some more work. I'm pretty sure I'll need a somewhat simplified version of this. I have several dozen of these "settings/traits" I need to have defaults for.


    BTW, if you used an std::integral_constant ( or something similar, that is a type ) instead of an enum you could use boost MPL to sort these kind of issues ( and much more ) ...
    Boost is not an option. I cannot expect users of my library to have boost installed. As popular as boost is (or seems to be), there is also a very strong opposition against it or at least parts thereof.
    I know a lot of the TR1 stuff derives from work done at boost. and I'm using some of that new stuff (like array) already, but that's part of STL now and they can't complain on that.

    Before I started with this, I did have a survey done at the potential customer base, and "no boost dependency" was an obvious conslusion.

  6. #6
    Join Date
    Oct 2008
    Posts
    1,456

    Re: change behaviour of a template class depending on the type ?

    Quote Originally Posted by OReubens View Post
    I'm pretty sure I'll need a somewhat simplified version of this. I have several dozen of these "settings/traits" I need to have defaults for.
    if they are all enum's, just make a macro ( AFAIK, there's no cleaner way of doing it (*) ):

    Code:
    // your_enum_trait.h
    
    namespace details
    {
    
    template <int>
    struct SelectOverload_ { typedef bool Bool; };
    
    }
    
    #define MAKE_ENUM_TRAIT( NAME, DEFAULT )			\
    													\ // don't have time for proper syntax&formatting ! :)
    	namespace details
    	{
    
    	template <class T,bool>
    	struct Get##NAME##_ { enum { NAME = DEFAULT }; };
    
    	template <class T>
    	struct Get##NAME##_ <T,true> { enum { NAME = T::NAME }; };
    
    	template <class T>
    	bool* Has##NAME##_( char );
    
    	template <class T>
    	bool Has##NAME##_( typename SelectOverload_<T::NAME>::Bool );
    
    	}
    
    	template <class T>
    	struct Get##NAME
    	{
    		enum { NAME = details::Get##NAME##_< T, sizeof( details::Has##NAME##_<T>(true) ) == sizeof( true ) >::NAME };
    	}
    
    
    // some_other_header.h
    
    namespace MyNamespace
    {
    
    MAKE_ENUM_TRAIT( ID, 1 )
    MAKE_ENUM_TRAIT( SIZE, 100 )
    // ...
    
    }
    (*) BTW, my solution should be roughly the same of the boost one, but AFAIR, boost solution is more refined in the way it handles error conditions ( say, what should happen when a type has a member type named ID instead of an enum item with the same name ? ).

  7. #7
    Join Date
    Oct 2008
    Posts
    1,456

    Re: change behaviour of a template class depending on the type ?

    Quote Originally Posted by OReubens View Post
    I did have a survey done at the potential customer base, and "no boost dependency" was an obvious conslusion.
    just for the sake of discussion, may I ask you what was the rationale behind their boost aversion?

  8. #8
    Join Date
    Apr 2000
    Location
    Belgium (Europe)
    Posts
    4,626

    Re: change behaviour of a template class depending on the type ?

    Quote Originally Posted by superbonzo View Post
    just for the sake of discussion, may I ask you what was the rationale behind their boost aversion?
    You would have to ask said customers. I do not know, and motivations for their requests/preferences wasn't part of the questionnaire. I assume they have a reason, whatever that may be.
    The amount of requests for no boost dependencies was obvious enough for us to do our best to avoid using it. So far, we didn't even have any need for it, nor is there anything so far that would make any part of our work significantly easier.

    Granted, the target audience for this particular project is a-typical. Which may make the aversion greater than you would expect to find in the average C++ populace.

    if we do end up needing parts of boost, we'll probably copy the portions and make them part of our lib (with proper crediting) rather than expect users to have an install of boost.

  9. #9
    Join Date
    Apr 2000
    Location
    Belgium (Europe)
    Posts
    4,626

    Re: change behaviour of a template class depending on the type ?

    Quote Originally Posted by superbonzo View Post
    BTW, if you used an std::integral_constant ( or something similar, that is a type ) instead of an enum you could use boost MPL to sort these kind of issues ( and much more ) ...
    It doesn't have to be an enum.
    Just ha d alook at this integral_constant, and I see no reason why this wouldn't be equally usable.

    I guess the reason why a class like array<type,size> ends up storing the size in an enum is only because by being an enum it is guaranteed to take no space in the image, whereas with a static const int, this guarantee would not be there?

    The compiler/linker may eventually decide the type isn't needed and strip it from the resulting executable image, but if you would take the address of said contant, there would be a variable somewhere.

    I don't see any problems with the ID being an int or some other type rather than an enum. I'm not sure yet how that would make things easier though. time for more experimentation... I think I'm starting to get the principle of this sfinae stuff.

  10. #10
    Join Date
    Apr 2000
    Location
    Belgium (Europe)
    Posts
    4,626

    Re: change behaviour of a template class depending on the type ?

    Finally did manage to wrap my head around this SFINAE bussiness, thanks to this explanation I had my Aha-erlebnis (or Eureka moment).


    Although I have decided against the use for the "configuration" thing (for now, I may revisit it). I ended up needing SFINAE for another part anyway.
    Though I have to say... this entire SFINAE stuff is awfully close making code incomprehensible for anyone but yourself (and maybe even yourself a few years down the road).



    I did notice there's a std::true_type and std::false_type ... Are they in any way related to using SFINAE ?

  11. #11
    Join Date
    Oct 2008
    Posts
    1,456

    Re: change behaviour of a template class depending on the type ?

    Quote Originally Posted by OReubens View Post
    Though I have to say... this entire SFINAE stuff is awfully close making code incomprehensible for anyone but yourself (and maybe even yourself a few years down the road).
    actually, SFINAE is just a mechanism for overload resolution of function templates, so, it's neither limited to nor used primarily as a method of writing trait classes. That said, even for overload resolution it has been superseded by more advanced techniques, like tag dispatching, which are more elegant, readable and above all more predictable. Indeed, SFINAE is known to behave erratically among compilers in some cases ...

    so, IMHO it's useful for relatively simple overload resolution scenarios, especially if used in conjunction with type traits and the new auto+decltype syntax, or for writing such "code-introspection" traits like mine in post #3, hidden behind a macro ( at least until <concepts> will be introduced in c++ ).

    Quote Originally Posted by OReubens View Post
    I did notice there's a std::true_type and std::false_type ... Are they in any way related to using SFINAE ?
    these are just integral constants of boolean type. Integral constants are just a way of having a standard type for each scalar constant; this is useful to simplify and uniformize type traits and metaprogramming.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  





Click Here to Expand Forum to Full Width

Featured