Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Variable name from template argument?

Is there any way to generate the name of a variable inside a class based on a template argument?

template<class T, (someconstruct) t>

class Item {
    public:        
        T t;
};

Item<float, "Position"> myItem;

myItem.Position = 0.123f;

Such that what I have instantiated is a variable of type T, with the identifier t (where t is passed in by the programmer, i.e. Position, so we have a T called Position? Or is this stretching the template meta-programming concept a little too far? :p

like image 338
Robinson Avatar asked Dec 14 '10 21:12

Robinson


3 Answers

No, not with that syntax, but you CAN create a setup similar to what you're trying to do:

template < typename Field >
struct field_value
{
  typename Field::type value;
};

template < typename Seq >
struct funky_struct : boost::mpl::inherit_linearly
  <
    Seq
  , boost::mpl::inherit
    <
      field_value< boost::mpl::placeholders::_2>
    , boost::mpl::placeholders::_1
    >
  >::type
{};

template < typename Field, typename Struct >
typename Field::type & get(Struct & s) { return static_cast< field_value<Field>& >(s).value; }

struct first_field { typedef int type; };
struct second_field { typedef char type; };

struct my_funky : funky_struct< boost::mpl::vector<first_field,second_field> > {};

...
my_funky f;
get<first_field>(f) = 23;

I leave allowing non-default construction to you. Also, with a minor amount of work this can be reflected and you can stick any amount of useful information about fields within.

like image 166
Edward Strange Avatar answered Oct 17 '22 23:10

Edward Strange


No, you can't achieve this with templates. Variable names (known as "identifiers") cannot be programatically manipulated by templates. Only the preprocessor can do that.

Regardless, this seems like a bad idea. Why do you want to do this?

like image 23
Charles Salvia Avatar answered Oct 17 '22 23:10

Charles Salvia


Something like what you want to achieve can be accomplished with inheritance. That is, the parent class has the variable name you want your template to have.

struct ItemNull {};

template <typename X, typename Y = ItemNull>
class Item : public X, public Y {};

template <typename T> struct HasPosition { T Position; };
template <typename T> struct HasMomentum { T Momentum; };

Item< HasPosition<float> > myItem1;
myItem1.Position = 0.123f;

Item< HasPosition<float>, HasMomentum<float> > myItem2;
myItem2.Position = 0.1f;
myItem2.Momentum = 0.2f;

The optional second argument allows for composition, as illustrated in myItem2. To add a third field, you could add to the tail, or expand from the front:

template <typename T> struct HasName { T Name; };

Item <
    HasPosition<float>,
    Item< HasMomentum<float>, HasName<std::string> >
> myItem3;
myItem3.Position = 0.1f;
myItem3.Momentum = 0.2f;
myItem3.Name = "Adam";

Item <
    Item < HasPosition<float>, HasMomentum<float> >,
    HasName<std::string>
> myItem4;
myItem4.Position = 0.1f;
myItem4.Momentum = 0.2f;
myItem4.Name = "Adam";

My personal preference is for the former of the two methods, because I find it to be a more intuitive way to extend it beyond 3 fields. The Item template syntax could probably be simplified using variadic template arguments.

The Has... templates could be machine generated or a macro could be created to make adding new fields a relatively simple task.

#define MAKE_HAS(X) template <typename T> struct Has##X { T X; }

MAKE_HAS(Position);
MAKE_HAS(Momentum);
MAKE_HAS(Name);
like image 5
jxh Avatar answered Oct 17 '22 22:10

jxh