Sign in to follow this  

[C++] template pointer to member

This topic is 3660 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

I have the following templates:
template < class Owner, typename Type, Type Owner::*Var >
struct Foo {}

template < class Descriptor >
struct Bar {
  typedef Descriptor::Owner owner;
  typedef Descriptor::Type type;
  typedef ??? // what is Var?
}

When used, they look something like this:
typedef Foo<MyClass, MyType, &MyClass::some_variable> FooVariable;

typedef Bar<FooVariable> BarVariable;

typename BarVariable::owner; // ok
typename BarVariable::type // ok

How can I get the pointer-to-member template parameter (Var) in BarVariable from Descriptor parameter?

Share this post


Link to post
Share on other sites
Foo is the descriptor of a class member (variable) - just the template

Bar is the accessor which gets instantiated to allow changing of the value on a particular instance.

First version was this:

template < class Owner, typename Type, Type Owner::*Var >
struct Bar
{
Bar( Owner *optr ) : o(optr) {}

operator Type &()
{
return o->*Var;
}

Owner *o;
};


The problem comes that I need to separate accessors from descriptors. Which results in second version:
template < class Descriptor >
struct Bar
{
typename Descriptor::owner Owner;

Bar( Owner *optr ) : o(optr) {}

operator Type &()
{
// Here be dragons
}

Owner *o;
};


I'm getting compiler errors with second version, since I can't figure out how to reference the original pointer. Trying to do Descriptor::var or similar apparently confuses the compiler, and it wants to access 'var' member of 'Descriptor'. typename Descriptor::var doesn't seem to work.

Doing this in descriptor however works:

template < class Owner, typename Type, Type Owner::*Var >
struct Foo{

static Type &get( Owner *instance )
{
return instance->*Var;
}
};

...
template < class Descriptor >
struct Bar {
...
operator Type &()
{
return Descriptor::get(o);
}

};


But I'd prefer to avoid static functions, and resolve types directly.

Share this post


Link to post
Share on other sites
If I understand you correctly you either have to make it a static const member, or use a static member function like you have. I prefer the static member function, I think it's quite descriptive as it is.

Share this post


Link to post
Share on other sites
Programmer Generals Warning: The following code is known to make grown people cry. You've been warned.

Anyway, this is what I've been mucking around with - compile time reflection/RTTI with no overhead. Uses Loki library.

Macros in this file will get mangled, I don't know how to get around this editor "feature".
rgen.hpp
#ifndef RGEN_HPP
#define RGEN_HPP

struct DefaultClassInfo
{
static const std::string &name() {
static const std::string n("default_name");
return n;
}
};


#define DESCRIPTOR(TYPE, NAME) struct REFLECTION_##NAME##_INFO { static const std::string &name() { static const std::string n(#NAME); return n; } }; typedef FieldDescriptor<Storage, TYPE, &Storage::NAME, REFLECTION_##NAME##_INFO > REFLECTION_##NAME##_DESCRIPTOR

#define ACCESSOR(NAME) typedef Field<REFLECTION_##NAME##_DESCRIPTOR, Loki::TL::IndexOf<RTTI, REFLECTION_##NAME##_DESCRIPTOR>::value > REFLECTION_##NAME##_ACCESSOR; REFLECTION_##NAME##_ACCESSOR NAME() { return REFLECTION_##NAME##_ACCESSOR(&storage); }


// REFLECTION FOR 1 MEMBER
#define REFLECT1( BASE, TYPE1, NAME1 ) protected: struct Storage : public BASE##::Storage { TYPE1 NAME1; }; Storage storage; DESCRIPTOR(TYPE1, NAME1); public: typedef Loki::TL::Append< BASE##::RTTI, REFLECTION_##NAME1##_DESCRIPTOR >::Result RTTI; ACCESSOR(NAME1);

// REFLECTION FOR 2 MEMBERS
#define REFLECT2( BASE, TYPE1, NAME1, TYPE2, NAME2 ) protected: struct Storage : public BASE##::Storage { TYPE1 NAME1; TYPE2 NAME2; }; Storage storage; DESCRIPTOR(TYPE1, NAME1); DESCRIPTOR(TYPE2, NAME2); public: typedef LOKI_TYPELIST_3( REFLECTION_##NAME1##_DESCRIPTOR, REFLECTION_##NAME2##_DESCRIPTOR ) LocalRTTI; typedef Loki::TL::Append< BASE##::RTTI, LocalRTTI >::Result RTTI; ACCESSOR(NAME1); ACCESSOR(NAME2);

// REFLECTION FOR 3 MEMBERS
#define REFLECT3( BASE, TYPE1, NAME1, TYPE2, NAME2, TYPE3, NAME3 ) protected: struct Storage : public BASE##::Storage { TYPE1 NAME1; TYPE2 NAME2; TYPE3 NAME3; }; Storage storage; DESCRIPTOR(TYPE1, NAME1); DESCRIPTOR(TYPE2, NAME2); DESCRIPTOR(TYPE3, NAME3); public: typedef LOKI_TYPELIST_3( REFLECTION_##NAME1##_DESCRIPTOR, REFLECTION_##NAME2##_DESCRIPTOR, REFLECTION_##NAME3##_DESCRIPTOR ) LocalRTTI; typedef Loki::TL::Append< BASE##::RTTI, LocalRTTI >::Result RTTI; ACCESSOR(NAME1); ACCESSOR(NAME2); ACCESSOR(NAME3);


#endif RGEN_HPP





#include <loki/typelist.h>
#include <loki/typelistmacros.h>

#include <iostream>
#include <vector>
#include <map>
#include <string>

#include "rgen.hpp"


template < class Parent, typename Type, Type Parent::*Var, class Info = DefaultClassInfo >
struct FieldDescriptor {
typedef Parent parent;
typedef Type type;
typedef Info info;

static void set( Parent *instance, Type &value )
{
instance->*Var = value;
}

static Type &get( Parent *instance )
{
return instance->*Var;
}
};

template < class Descriptor, unsigned int Index >
class Field {
public:
typedef typename Descriptor::parent Parent;
typedef typename Descriptor::type Type;


Field( Parent *ptr )
: p(ptr)
{}

void set(Type value)
{
Descriptor::set(p, value);
p->dirty<Descriptor, Index>();
}

operator Type &()
{
return Descriptor::get(p);
}

private:
Parent *p;
};


template < class T >
class Reflection
{
public:
template < unsigned int Index >
static const std::string name()
{
return Loki::TL::TypeAt<typename T::RTTI, Index>::Result::info::name();
}
};

class DeltaBase
{
public:
template < class Descriptor, unsigned int Index >
void dirty()
{
static int i = 0;
i++;
std::cout << "Variable changed: " << Descriptor::info::name() << std::endl;
}
};

class ReflectionBase
{
protected:
struct Storage : public DeltaBase {
};
typedef Loki::NullType RTTI;
};



Actual user-defined classes:
class Point : public ReflectionBase
{
REFLECT3(
ReflectionBase,
int, x,
int, y,
char, z);
};

class Quad : public Point
{
REFLECT1(
Point,
float, w
);
};





int main(int argc, char* argv[])
{
Point pp;
Quad qq;
pp.x().set(1);
pp.y().set(2);
pp.z().set(3);

std::cout << pp.x() << std::endl;
std::cout << pp.y() << std::endl;
std::cout << pp.z() << std::endl;

typedef Reflection<Point> R;
std::cout << R::name<0>() << std::endl;
std::cout << R::name<1>() << std::endl;
std::cout << R::name<2>() << std::endl;

qq.x().set(10);
qq.y().set(20);
qq.z().set(30);
qq.w().set(3.14f);
std::cout << qq.x() << std::endl;
std::cout << qq.y() << std::endl;
std::cout << qq.z() << std::endl;
std::cout << qq.w() << std::endl;

return 0;
}




From what i've managed to verify, the access to members is without overhead, same as accessing variables directly.

But!

Even when using plain types, setting a variable triggers an event, and transmits the index of the variable that was changed.

Variables can also be looked up at compile time by the same index, allowing for, obviously, partial serialization (networking, ORM).

Running the code above produces:
Variable changed: x
Variable changed: y
Variable changed: z
1
2
&#9829;
x
y
z
Variable changed: x
Variable changed: y
Variable changed: z
Variable changed: w
10
20
&#9650;
3.14



which demonstrates how, even though PODs are used, each variable notifies its containing class (not really, it notifies the storage class, but that's just a matter of pointer, just didn't go changing it yet).

Same function could be used to trigger events and notify subscribers of value changes.

This is essentially a zero overhead (unless there's some hidden somewhere, it should be literally zero), which is equivalent to hard-coding getters/setters and accessing the variables through containing class only, in a set(index, value) manner.

Not that the code is experimental, and was merely used as proof of concept.

The production value of such approach is... well, to each their own. It's simply a concept of compile-time properties, each uniquely identifiable during run-time, with hopefully no overhead.

Share this post


Link to post
Share on other sites

This topic is 3660 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.

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