Jump to content
  • Advertisement
Sign in to follow this  
hplus0603

template trickery

This topic is 4418 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Check out this code, which compiles fine:

template<typename T> struct Trait {
  typedef T Type;
};

struct Base {
  template<typename T> typename Trait<T>::Type get() const;
};

template<typename T> struct Derived : public Base {
  template<> typename Trait<T>::Type get<T>() const { return 0; }
};

Now contrast to this code, which gives a "; missing before <" error in the struct Derived definition of get():

template<typename T> struct Trait {
  typedef T Type;
};

struct Base {
  template<typename T> typename Trait<T>::Type get() const;
};

template<typename T, typename B> struct Derived : public B {
  template<> typename Trait<T>::Type get<T>() const { return 0; }
};

I can sort-of see why -- the compiler doesn't know that I intend to pass a "base" in as type B, so it doesn't know that get<>() is an explicit specialization. How can I work around this, other than adding Base as an explicit base class to the Derived template?

Share this post


Link to post
Share on other sites
Advertisement
I should add that the following also doesn't work:


template<typename T> struct Trait {
typedef T Type;
};

struct BaseBase {
template<typename T> typename Trait<T>::Type get() const;
};

struct Base : public BaseBase {
};

template<typename T, typename B> struct Derived : public B, public BaseBase {
template<> typename Trait<T>::Type get<T>() const { return 0; }
};



The error I get (from MSVC 2005) is:
error C2912: explicit specialization; 'const float &Derived<T,B>::get(void) const' is not a specialization of a function template
I would argue that it actually IS a specialization of a function template.

Share this post


Link to post
Share on other sites
Neither version of your code compiles with gcc 4.1.0.

The function declaration

template<typename T> typename Trait<T>::Type get() const;

won't work, since the return value is not part of the function signature.
Did you actually try to call/instantiate this function ?
For instance:

void foo()
{
Base bi;
int ib = bi.get();

Base bf;
float fb = bf.get();

Derived<int> di;
int id = di.get();

Derived<float> df;
float fd = df.get();
}


The get() function of the Derived class will work, since there is just one version generated which depends on the template type of the Derived class.
The get() function of the Base class will not work, since many versions of this function may be generated and the return value is not used to select one. Either you can explicitly tell the compiler which function to use (i.e. get<int>()) or you can add a parameter which selects the function:

template<typename T> typename Trait<T>::Type get(T&) const;

Share this post


Link to post
Share on other sites
I don't think it can be done directly, but this works as I think you want. I'm sure that some light sprinkling of Boost might help clean it a bit.

#include <iostream>

template<typename T> struct Trait {
typedef T Type;
};

struct Base {
template<typename U> typename Trait<U>::Type get() const
{
return 1;
}
};

template<typename T, typename B, typename U> struct helper;

template<typename T, typename B> struct Derived : public B
{
template<typename U> typename Trait<U>::Type get() const
{
return helper<T,B,U>::get(*this);
}

typename Trait<T>::Type get_T() const { return 0; }
};

template<typename T, typename B, typename U> struct helper
{
static typename Trait<U>::Type get( const Derived<T,B>& d )
{
return d.B::template get<U>();
}
};

template<typename T, typename B> struct helper<T,B,T>
{
static typename Trait<T>::Type get( const Derived<T,B>& d )
{
return d.get_T();
}
};


int main()
{
Derived<int,Base> x;
std::cout << x.get<int>() << std::endl;
std::cout << x.get<double>() << std::endl;
}


Share this post


Link to post
Share on other sites
OK, the C++ Standard explicitely [rolleyes] disallow what you want to do in clause 14.7.3-18:

Quote:
In an explicit specialization declaration for a member of a class template or a member template that appears in namespace scope, the member template and some of its enclosing class templates may remain unspecialized, except that the declaration shall not explicitely specialize a class member template if its enclosing class templates are not explicitely specialized as well. ...


I think it works when you specify Base as the base class, because then you are working with a member of Base, which isn't a template.

Share this post


Link to post
Share on other sites
Quote:
Original post by nmi
Neither version of your code compiles with gcc 4.1.0.

The function declaration

template<typename T> typename Trait<T>::Type get() const;

won't work, since the return value is not part of the function signature.


The return type isn't part of the signature, but the template parameters still allow to discriminate. You can explicitely call get<int>() without relying on template argument deduction.

Share this post


Link to post
Share on other sites
Quote:
Original post by Fruny
The return type isn't part of the signature, but the template parameters still allow to discriminate. You can explicitely call get<int>() without relying on template argument deduction.


That's what I wrote in the last sentence.

Share this post


Link to post
Share on other sites
Thanks Fruny. That doesn't quite do what I want, though.

What I want to do is create structures with metadata to get around the lack of introspection and reflection in C++. The types that I really want to specialize on are the "member names" of the struct. The "helper" route doesn't quite get me there (although I'll cook on it some more).

Share this post


Link to post
Share on other sites
Here's a solution to what I actually want to do. I want to declare "members" as type names, and declare structs as aggregates of these members. This allows me to do compile-time reflection to do things like generate visitor for marshalling, etc.


// Declare my "member types" (this can be macro-ized)
template<typename T> struct Traits;
struct foo;
template<> struct Traits<foo> {
typedef unsigned short Type;
};
struct bar;
template<> struct Traits<bar> {
typedef float Type;
};

// Declare helpers in the system
template<typename N> struct Member {
typename Traits<N>::Type _memb;
};
struct Base {
};
template<typename N, typename B = Base> struct Instance : B, Member<N> {
template<typename T> typename Traits<T>::Type const & get() const {
return Member<T>::_memb;
}
template<typename T> void set(typename Traits<T>::Type const & t) {
Member<T>::_memb = t;
}
};

// Declare an actual structure used by a user
struct X :
public Instance<foo, Instance<bar> >
{
};

int main(int argc, char* argv[])
{
X x;
x.set<foo>(10):
x.set<bar>(20);
x.get<foo>();
x.get<bar>();
return 0;
}




So, I'm now happy again.

Share this post


Link to post
Share on other sites
Quote:
Original post by hplus0603
Here's a solution to what I actually want to do. I want to declare "members" as type names, and declare structs as aggregates of these members.

Sounds like you want a Boost.Fusion map, which was formally accepted into Boost in May and will be included with Boost in 1.34. For now, you can grab it here. Documentation is in the libs directory.

As an example, you can accomplish what you want in Boost.Fusion as:


#include <boost/fusion/sequence/container/map.hpp>

struct foo;
struct bar;

int main()
{
using namespace ::boost::fusion;

map< pair< foo, unsigned short >, pair< bar, float > > your_map;

at< foo >( your_map ) = 10;
at< bar >( your_map ) = 20;
}

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!