I have an Entity class that contains some attributes (pieces of data). These attributes are stored in a map from name => value.
class Entity
{
public:
// Sets the attribute with the specified name.
void attribute(const std::string& name, const GenericType& value) {
m_attributes[name] = value;
}
// Returns the attribute with the specified name.
GenericType& attribute(const std::string& name) {
return m_attributes[name];
}
template<typename AttributeType>
void attribute(const std::string& name, const AttributeType& value) {
m_attributes[name] = GenericType(value);
}
template<typename AttributeType>
AttributeType& attribute(const std::string& name) {
return generic_cast<AttributeType>(m_attributes[name]);
}
private:
// Map of attributes from name => value.
std::unordered_map<std::string, GenericType> m_attributes;
}
I have a method to create or overwrite an attribute and another method that returns the attribute with the specified name. The first two methods are exposed to Lua. The last two methods are for accessing and modifying attributes from my c++ source code. The problem is that attributes can be of any type. For example, I want to be able to do:
entity.attribute("Health", 100)
entity.attribute("Position", Vector3(1,2,3))
entity.attribute("Position").x = 4
It should be possible to read and modify attributes both from my c++ source files and from lua scripts. I have previously been using ChaiScript where I used the Boxed_Value class as my GenericType. This worked well but excessive compile times have forced me to look elsewhere.
Is there a way to achieve this with LuaBind (or any other Lua binding library)? The luabind::object class looks promising but it takes a lua_State pointer in its constructor. This worries me as I feel that the Entity class really shouldn't know anything about the Lua state.
In LuaBind your entity class doesn't need to know anything about lua state. A Lua binding does not have to be 1-to-1 syntactically identical to the C++ API, as these are quite different languages.
In your case, I would rather split the api into getters and setters. With the shown Entity class you might get struggling with making LuaBind do what you want to do unambiguously. What you could do is write a wrapper class for Entity on the C++ side, which would have a LuaBind-conforming, simple interface, i.e. splitting getters and setters using unambiguous names.
Blufs is an example, demonstrating what I mean.
As a simple example, with LuaBridge:
somewhat tricky to create a simple, non hand-rolled binding:
class Entity {
std::map<std::string, int> attributes;
public:
void attribute(std::string const& key,int value) {
attributes[key] = value;
}
int& attribute(std::string const& key) {
return attributes[key];
}
};
a wrapper can be bound instead:
class EntityWrapper {
Entity entity;
public:
void set_int(std::string const& key,int value) {
entity.attribute(key,value);
}
int get_int(std::string const& key) {
return entity.attribute(key);
}
};
a simple binding:
void luabridge_bind(lua_State *L) {
luabridge::getGlobalNamespace(L)
.beginClass<EntityWrapper>("Entity")
.addConstructor<void(*)(), RefCountedPtr<EntityWrapper> /* creation policy */ >()
.addFunction("get_int", &EntityWrapper::get_int)
.addFunction("set_int", &EntityWrapper::set_int)
.endClass()
;
}
and in Lua:
local e = Entity()
e:set_int("bla",42)
print(e:get_int("bla"))
If you need Entity to interact with other APIs, write small wrappers that get the original wrapped object and pass it to other functions.
Related
I'm writing a class that needs to store a bunch of different primitives and classes. I've decided to make a map for each different data type where the key in the map would be the name of the variable, and the value in the map would be the value of the variable. My maps are defined like this:
std::unordered_map<std::string, int> myInts;
std::unordered_map<std::string, float> myFloats;
std::unordered_map<std::string, Something> mySomethings;
For each map, I have to write two methods, one which will get the value of some variable and one which will set the value of some variable like so:
void setMyInt(const std::string &varName, int newVal) {
myInts[varName] = newVal;
}
int getMyInt(const std::string &varName) {
return myInts[varName];
}
This is all fine and easy, however, I ended up with 8 different maps, and 16 of these get set methods. This doesn't seem very efficient or clean to me, not to mention that every time I need to store a new data type I have to define a new map and write 2 new get-set methods.
I considered getting rid of the get set methods and instead writing 2 template methods which would take in the type of the variable which the user needs to get or set, and then accessing the proper set to perform the operation, like so:
template<class Type>
void getVariable<Type>(const std::string &varName) {
// if Type == int -> return myInts[varName];
// else if Type == float -> return myFloats[varName];
}
This seems like a really poor approach since the user could pass in types which are not supported by the class, and the method breaks C++'s rule of not being really generic.
Another idea I had was writing some Variable class which would have all of the fields that this class should store, along with some enum that defines what Variable the class is actually being used for, and then making a map of this Variable class like this:
enum Type {
TYPE_INT,
TYPE_FLOAT,
TYPE_SOMETHING
class Variable {
Type type;
int myInt;
float myFloat;
Something mySomething;
}
std::unordered_map<std::string, Variable> myVariables;
But this also seems like a really poor solution, or at least one which is difficult to understand. Is there some smart way to make this class store different types?
How about a template class like below:
template<typename ValueType>
class UnorderedStringMap : public std::unordered_map<std::string, ValueType> {
public:
...
void setValue(const std::string &varName, ValueType newVal) {
std::unordered_map::operator[](varName) = newVal;
}
const ValueType& getValue(const std::string &varName) {
return std::unordered_map::operator[](varName);
}
...
}
UnorderedStringMap<int> myInts;
UnorderedStringMap<float> myFloats;
You can then use it as a normal std::unordered_map as well.
I've been reading about Lua/C++ and I had a question about design I was hoping I could get some help on.
What I want:
I want so my class in C++ (Created and Destroyed by C++) to be able to call
a Lua function using itself as a parameter.
Eg.
object.h
class Object
{
public:
Object(const std::string & onLoad, const std::string & onEvent);
~Object();
void OnLoad();
void RegisterEvent(const std::string & eventID);
void OnEvent(const std::string & eventID);
void SetValue(int value);
private:
int m_value;
std::string m_onLoad;
std::string m_onEvent;
};
object.cpp
Object::Object(const std::string & onLoad, const std::string & onEvent)
: m_value(0)
, m_onLoad(onLoad)
, m_onEvent(onEvent)
{
}
Object::~Object()
{
GAME->GetEventManager()->UnregisterListener(this);
}
void Object::OnLoad()
{
//
// call lua function [name store in: m_onLoad]
// eg. m_onLoad = Object_OnLoad
// in lua ->
// function Object_OnLoad(object)
//
}
void Object::RegisterEvent(const std::string & eventID)
{
GAME->GetEventManager()->RegisterEvent(this, eventID);
}
void Object::OnEvent()
{
//
// call lua function [name store in: m_onEvent]
// eg. m_onEvent = Object_OnEvent
// in lua ->
// function Object_OnEvent(object, eventID)
//
}
void Object::SetValue(int value)
{
m_value = value;
}
script.lua
function Object_OnLoad(object)
object:RegisterEvent("EVENT_CURRENT_HEALTH_CHANGED")
end
function Object_OnEvent(object, eventID)
if (eventID == "EVENT_CURRENT_HEALTH_CHANGED")
object:SetValue(GetCurrentHealth());
end
test.cpp
Object *pTest = new Object("Object_OnLoad", "Object_OnEvent");
pTest->OnLoad();
GAME->GetEventManager()->TriggerEvent(CEvent("EVENT_CURRENT_HEALTH_CHANGED"));
delete pTest;
After Some reading:
From what I've read this is no direct way to assign C++ class instance functions.
Non-member functions are needed. Tables are used to track functions.
My Questions:
What do I push as an argument when calling the Lua functions (Object_OnEvent(object, eventID) etc...) Is it a pointer to the object
How does Lua know the object design
Do I need a table per object or instance
Do I need to duplicate all the functions I intend to use in Lua again as normal functions grabbing a the ptr to call it from
As a final and possible single question:
Is there any place I could get more information on what I'm trying to achieve described above.
I'm probably just going to go back to step one and try and absorb this information again.
I still wan't to make my post tho. I'll post back myself if I set it up.
There are many questions, but in principle, if I understand you correctly, you want to bind your C++ classes to Lua, have a shared object lifetime and automatic garbage collection, and be able to call Lua functions on objects created on the C++ side.
This is all possible with either low-level glue code, or dedicated binding libraries, such as LuaBridge and LuaState. LuaState is used in my answer for convenience and fast prototyping.
What's not yet clear is why you want to define a trivial function in Lua, such as Object_OnLoad to call it from C++, which would call a method of an object that you have created in the same scope on the C++ side. I'd guess, you have a more complicated picture in your code, so that such Lua usage pattern would be justified. In that case, one by one:
The ingredients
Binding a class to Lua
Here's a declarative binding that you can call once before calling any other Lua functions
void luabridge_bind(lua_State *L) {
luabridge::getGlobalNamespace(L)
.beginClass<MyObject>("MyObject")
.addConstructor<void(*)(), RefCountedPtr<MyObject> /* creation policy */ >()
.addFunction("RegisterEvent", &MyObject::RegisterEvent)
.endClass()
;
}
To perform the binding:
lua::State state;
luabridge_bind(state.getState());
Calling a lua function on a C++ side object
LuaState unfortunately cannot use objects in call parameters at the moment, while primitives work, i.e. from the readme:
state.doString("function add(x, y) return x + y end");
int result = state["add"](1,2);
But what one could do is to temporary create a global variable instance (watch out for name collisions) and call the function on it.
Preparing the script:
static const char *script =
"function Object_OnLoad(object)\n"
" object:RegisterEvent('EVENT_CURRENT_HEALTH_CHANGED')\n"
"end"
;
state.doString(script);
Creating an automatically lifetime-managed object:
auto my_obj = RefCountedPtr<MyObject>(new MyObject);
Calling the lua function on the object:
SetGlobal(state.getState(), "my_obj", my_obj);
state.doString("Object_OnLoad(my_obj); my_obj = nil");
Where SetGlobal can look like that:
template <typename T>
void SetGlobal(lua_State* L, const char *name, T value) {
luabridge::push(L, value);
lua_setglobal(L, name);
}
A complete example and comments
You can find the whole example code at Github: try_luabridge.cpp
which has been compiled and run at Travis CI.
The possibilities are limitless. It's up to you how you structure your code, so, naturally, this answer won't provide code that would immediately fit your needs. However, I'd encourage you to read Programming in Lua, and LuaBridge and LuaState manuals to get a better overview of the possiblities that are at your hand.
I'm currently working on a college project with C++ and one of my assignments is to make a social network using inheritance and polymorphism. Currently I have a Node class that is used on a Map and Multimap (both are created manually and not used from the std). The node can hold two variables (key and data for example) and where I'm using it, the first variable can either be a pointer or a string (they let us use std::string).
The problem I'm having is that when I inherit from the "root" class (Object) and use "Object" as a data type for "key", I'm unable to pass a string created with the std as parameter to its constructor, because it doesn't inherit from my Object class. One solution is to implement my own string class and make it inherit from Object, but I was searching for other workarounds.
If there's any problem with the logic above, please tell me as I'm just beginning with C++.
EDIT 1 (some code for my Node):
class TempNode
{
private:
TempNode* next;
Key key;
T value;
public:
TempNode();
explicit TempNode(const Key thisKey, const T thisValue, TempNode* thisNext = NULL)
: key(thisKey)
, value(thisValue)
, next(thisNext)
{
}
inline Key getKey() { return key; }
inline T getValue() { return value; }
inline TempNode* getNext() { return next; }
inline void setNext(TempNode* thisNext) { next = thisNext; }
};
The string or Person types are currently used only in key, but that is with another implementation using templates (which works fine), but my teacher now requires us to apply inheritance to the entire project (to get used to it I guess).
To implement this using inheritance, you think of Key as a data type specifically designed as a key in your map/multimap implementation. Key inherits from Object, but it may provide its own, key-specific functions, such as – for example – a function repr() which generates a representation used by the map for some map-specific operations (maybe as a basis for hashing, or sorting or whatever).
The map/multimap must be used in such a way that the Key objects are stored as pointers (or std::unique_ptr, or std::shared_ptr, or whatever is appropriate), but not as copies of Key objects.
So we have:
struct Object
{
virtual ~Object()
{ }
};
/* Key class. Pointers of this type are inserted
into the map. */
class Key : public Object
{
public:
/* Must be supported by all keys: */
virtual std::string repr() const = 0;
};
We also assume there is a separate definition of Person objects:
struct Person : Object
{
Person(const std::string &name)
: name_(name)
{ }
std::string name_;
};
According to your specification, there are two flavours of Key: One that represents strings and must be initialized using a string, and another one that represents persons and must be initialized by a person pointer (I'll assume that the person-keys do not actually own these pointers, so you need to make sure the person objects they point to stay alive as long as the person-key exists).
We implement this by specializing Key into two derived classes, a PersonKey and a StringKey:
class PersonKey : public Key
{
public:
PersonKey(Person *person_ptr)
: Key() , person_ptr_(person_ptr)
{ }
virtual std::string repr() const
{
if (person_ptr_ != 0)
return std::string("Person/") + person_ptr_->name_;
else
return "<NUL>";
}
private:
Person *person_ptr_;
};
class StringKey : public Key
{
public:
StringKey(const std::string &str)
: Key() , str_(str)
{ }
virtual std::string repr() const
{
return str_;
}
private:
std::string str_;
};
When you make insertions into your map/multimap, you generate Key objects (which you represent as Key* or Key& or std::unique_ptr<Key>). When you want to insert a string, you generate them as StringKey objects, and when you want to insert them as person-pointers, you use PersonKey – but the data type of the key you insert will not reflect the specialization.
Here is an example of a general Key object (implemented as std::unique_ptr<Key>, but you may just use Key* if you are not afraid of memory leaks):
int main()
{
/* General key object: */
std::unique_ptr<Key> mykey;
/* Now it points to a string-key, initialized using
a string, as required: */
mykey.reset(new StringKey("hello"));
std::cout << "repr(mykey) == \""
<< mykey->repr()
<< '"'
<< std::endl;
/* Now the same key object is made to refer to
a person object: */
Person person("me");
mykey.reset(new PersonKey(&person));
std::cout << "repr(mykey) == \""
<< mykey->repr()
<< '"'
<< std::endl;
return 0;
}
Necessary headers for the code above are:
#include <iostream>
#include <memory>
#include <string>
(But memory is only required for my use of std::unique_ptr, which is not actually necessary to solve your problem.)
I think what you are really looking for are templates. Your solution with "root object" won't work as you can see with standard objects and external libraries but also you will not be able to use your containers with primitives (for example person id(as int) as key, and Person class as value).
With templates you can say what type you are going to work with at compile time and compiler will help you to obey your own rules. It can be declared like this:
template<class T1, class T2>
class Map{
T1 key;
T2 value;
(...)
}
Then you can use it more or less like this:
Map<std::String, int> phoneBook;
And compiler will guard you and warn, if you try to add, for example float instead of int, to you Map. But before you start coding I advice you to read some tutorials first, or maybe even some book on c++ in general. But if you want to start with generic right now, you can start her:
http://www.cplusplus.com/doc/tutorial/templates/
The only way you'd be able to store a string in your Object variable was if the string class inherited from your Object class, so you will have to implement your own String class unfortunately.
The real flaw here is that you are taking a Java/C# approach to design, where an Object variable can hold anything. In C++ the proper way to handle such things is through the use of templates, supposing your Map/Multimap/Node only need to hold one specific data type.
If your container needs to be able to hold any arbitrary data type, I would recommend using type erasure, although that can be a bit complicated for a beginner.
My goal is to access a class that is passed in as a parameter inside of myFunction.
Here's what I'm trying to do:
void myFunction(string myString)
{
callFunctionOn(OuterType::InnerType::myString);
}
I'm trying to call some function on something that's in a type. For example, my code in some other file might look like:
namespace OuterType {
namespace InnerType {
//stuff here
}
}
However, using myString in that way doesn't work. If myString holds the value "class1", then I want that callFunctionOn part to be interpreted as
callFunctionOn(OuterType::InnerType::class1);
I feel like this is super simple, but I've been programming all day and my mind grows tired...
SOLVED: It looks like in order to this in this way, I'd need a language with reflection. To solve this I took a different approach to the problem and passed in a pointer to the class instead.
C++ doesn't have reflection built in, but it does have pointers to data, functions, and class members. So you can use a std::map or unordered_set to find the pointer with a particular name (you have to add all the name/pointer pairs into the map beforehand).
Your solution is likely to look something like:
namespace Outer
{
namespace Inner
{
void funcA( void ) { std::cout << "called funcA" << std::endl; }
std::map< std::string, void (*)(void) > members;
}
}
// in some initialization function
Outer::Inner::members["funcA"] = &Outer::Inner::funcA;
// later
std::string myString = "funcA";
void (*f)(void) = Outer::Inner::members[myString]; // lookup function by name
(*f)(); // call function via its pointer
Of course the type of the pointer will probably need to change to meet your application requirements.
You're trying to access a variable based on a run-time string that contains its name? That's not possible; the names of variables disappear after compilation and linking. (Except insofar as they are kept around to facilitate debugging).
Do you mean :
OuterType::InnerType::callFunctionOn(myString);
maybe this idea: operator() can take parameters, wrapping it in a class ine can make calls that are resolved in the overloaded operator() based on its parameters.
template<typename TypeSig, class InstanceOf, typename NA,typename Args>
class FuncMap {
public:
typedef TypeSig (InstanceOf:: *cbMethod) ( NA, Args );
FuncMap( InstanceOf & cInst, cbMethod cbM ) : mcInst(cInst) {mcbM = cbM;}
TypeSig operator() ( NA na, Args args) {return (mcInst.*mcbM)(na, args);}
private:
InstanceOf & mcInst;
cbMethod mcbM;
};
you need to build a map of runtime string values as keys and pointers to instance methods as seen above. i used this for re-dispatch tracing and custom runtime dispatch with lesser than RTTI overhead.
this allows you to have default, if no key found, or other logic as you wish.
Pointer to members are not used very much but they are really powerful, how have you used them and what's the coolest things you've done?
Edit:
This is not so much to list things that are possible, for example listing boost::bind and boost::function aren't good. Instead maybe a cool usage with them? I know they're very cool in themselves but that's not what this is about.
I once was needed to operate with criteria data as pure structure to be able to store the list of all criteria in the queue. And I had to bind the structure with the GUI and other filter elements, etc. So I came up with the solution where pointers to members are used as well as pointers to member functions.
Let's say you have an
struct Criteria
{
typedef std::string Criteria::* DataRefType;
std::string firstname;
std::string lastname;
std::string website;
};
Than you can wrap criteria field and bind with the string representation of the field with
class Field
{
public:
Field( const std::string& name,
Criteria::DataRefType ref ):
name_( name ),
ref_( ref )
{}
std::string getData( const Criteria& criteria )
{
return criteria.*ref_;
}
std::string name_;
private:
Criteria::DataRefType ref_;
};
Then you can register all the fields to use whenever you want: GUI, serialization, comparison by field names, etc.
class Fields
{
public:
Fields()
{
fields_.push_back( Field( "First Name", &Criteria::firstname ) );
fields_.push_back( Field( "Last Name", &Criteria::lastname ) );
fields_.push_back( Field( "Website", &Criteria::website ) );
}
template < typename TFunction >
void forEach( TFunction function )
{
std::for_each( fields_.begin(), fields_.end(),
function );
}
private:
std::vector< Field > fields_;
};
By calling for instance fields.forEach( serialization ); or
GuiWindow( Criteria& criteria ):
criteria_( criteria )
{
fields_.forEach( std::bind1st(
std::mem_fun( &GuiWindow::bindWithGui ),
this ) );
}
void bindWithGui( Field field )
{
std::cout << "name " << field.name_
<< " value " << field.getData( criteria_ ) << std::endl;
};
The coolest thing I've done with them, I did a long time ago. There's probably a better way to do it today.
I created a self-generating command-line parser for a network management tool. The classes representing the objects to be managed each had their own tables of sub-classes (name, pointer-to-factory-member), instances (id, pointer to instance from a list), and commands (name, pointer to member function). This allowed the parser to handle things like:
SET NETWORK ROUTE 192.168.0.0 HOPS 1
or
QUERY NETWORK NAMESERVER servername
without knowing anything about routes, or name servers.
Pointers to member function are great for creating psuedo lamba expressions with for_each
vector<SomeClass*> v = getAVector();
for_each(v.begin(), v.end(), mem_fun(&SomeClass::print));
Well I use pointers to member functions regularly with standard algorithms. There is nothing special about them as far as I am concerned.
You can bind member variables and functions with boost::bind and get usual functor.
Next work with them will like on usual functions usage:
passing as callback or signal function;
using in standard algorithms;
using as comparator in std::map/set;
using as data accessors;
In addition to the previous, you can for example use them as callback functions.
I did it in a "DomainEditor" class for this huge application I wrote. All my type (domain) tables in the database could be edited by the admins of the program and since the clients called some of the types by different names than others did, I made a dialog that allowed you to edit them. Well, I didn't want to make an editor for the 15+ domain types, so I wrote a super-class that I could cast each class to, and using pointers I could make simple calls to each domain table. Each one supported all the same properties, a Description (name), an ID, an Inactive flag, and a Required flag. So, the code started with a Macro to setup my calls:
#define DomainList(Class, Description, First, Next, Item, UpdateItem, DeleteItem, IsItemRequired, MaxLength) { \
CWFLHandler *handler = new CWFLHandler; \
handler->pWFL = new Class;\
handler->LoadFirstType = (LoadFirst)&Class::First;\
handler->LoadNextType = (LoadNext)&Class::Next;\
handler->LoadType = (Load)&Class::Item;\
handler->UpdateType = (Update)&Class::UpdateItem;\
handler->DeleteType = (Delete)&Class::DeleteItem;\
handler->IsRequiredType= (IsRequired)&Class::IsItemRequired; \
handler->MAX_LENGTH = MaxLength;\
PopulateListBox(m_Domain, Description, (long)handler); }\
Then, lots of calls to the Macro: (Here is just a single one)
DomainList(CConfigWFL, "Application Parameter Types", LoadFirstParameterType, LoadNextParameterType, LoadParameterTypeByTypeId, UpdateParameterType, DeleteParameterType, IsParameterTypeRequired, LEN_APPL_PARAMETER_DESC);
Then, the calls to edit the data were all common, and I didn't have to duplicate any code at all...
For example, to populate the list with the selected item in the DropDownList (populated by the Macro), the code would look like this:
if((pWFLPtr->pWFL->*pWFLPtr->LoadFirstType)(true))
{
do
{
m_Grid.AddGridRow();
m_Grid.SetCheck(COLUMN_SYSTEM, (pWFLPtr->pWFL->*pWFLPtr->IsRequiredType)(pWFLPtr->pWFL->TypeId));
m_Grid.SetCheck(COLUMN_STATUS, pWFLPtr->pWFL->InactiveIndc == false);
m_Grid.AddTextToGrid(COLUMN_NAME, pWFLPtr->pWFL->TypeDesc);
m_Grid.AddTextToGrid(COLUMN_DEBUG, pWFLPtr->pWFL->TypeId);
m_Grid.AddTextToGrid(COLUMN_ID, pWFLPtr->pWFL->TypeId);
}
while((pWFLPtr->pWFL->*pWFLPtr->LoadNextType)());
Of, course, this was all stored in a class that was part of the dialog. And I simply created new instances of the class, stored them in the ListBox's ItemData member. So, I did have to clean all that up when the dialog closed.. I left that code out of this message however.
The class to store all this stuff in was defined as:
typedef bool (CMyWFL::*LoadFirst)(bool);
typedef bool (CMyWFL::*LoadNext)();
typedef bool (CMyWFL::*Load)(long);
typedef bool (CMyWFL::*Update)(long, const char*, bool);
typedef bool (CMyWFL::*Delete)(long);
typedef bool (CMyWFL::*IsRequired)(long);
class CWFLHandler {
public:
CWFLHandler() {};
~CWFLHandler() { if(pWFL) delete pWFL; }
CMyWFL *pWFL;
LoadFirst LoadFirstType;
LoadNext LoadNextType;
Load LoadType;
Update UpdateType;
Delete DeleteType;
IsRequired IsRequiredType;
int MAX_LENGTH;
};
CWFLHandler *pWFLPtr;
All this work made it really nice to be able to add new domains to the application with very little work to add them to the domain editor... There may have been a better way, I don't know. But this is the way I went, and it worked VERY well for me, and IMHO, it was very creative... :)
I used them as part of a StructSerlialiser to populate C++ POD structures from SAX Parser events, i.e. to map XML onto a C++ data model.
template<class STRUCT, typename FIELDTYPE>
struct FieldBinderImpl : public FieldBinder<STRUCT>
{
typedef FIELDTYPE (STRUCT::*MemberPtr);
FieldBinderImpl (const std::string& tag, MemberPtr memberPtr)
: FieldBinder (tag)
, memberPtr_ (memberPtr)
{
}
virtual SerialiserBase* createSerialiser (STRUCT& data) const
{
return new Serialiser<FIELDTYPE> (&(data.*memberPtr_));
}
private:
MemberPtr memberPtr_;
};
template<class T>
class StructSerialiser : public SerialiserData<T>
{
public:
typedef std::vector<FieldBinder<T>*> FieldBinderList;
private:
static FieldBinderList fieldBinderList_;
protected:
template<typename FIELDTYPE>
static void bind (const std::string& tag, FIELDTYPE (T::* member))
{
fieldBinderList_.push_back (new FieldBinderImpl<T, FIELDTYPE> (tag, member));
if (tag.empty ())
fieldBinderList_.back ()->tags_ = Serialiser<FIELDTYPE>::getTags ();
}
// ...
}
// ...
Also had Serialisers for double, strings, vectors, etc. To use it you would just bind struct members to names, e.g.:
class Index
{
public:
std::string currency;
std::string name;
};
template<>
class Serialiser<Index> : public StructSerialiser<Index>
{
public:
Serialiser (Index* data) : StructSerialiser<Index> (data) {}
static void initialise ()
{
bind ("currency", &Index::currency);
bind ("name", &Index::name);
}
};