Created
March 16, 2017 06:16
-
-
Save eruffaldi/5070833dbf23f9f843b6859b266b7a4f to your computer and use it in GitHub Desktop.
Variable reflection using both dynamic object and member variable pointer
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <iostream> | |
#include <unordered_map> | |
#include <memory> | |
#include <vector> | |
#include <string> | |
#include <boost/lexical_cast.hpp> | |
#define TaskContext Holder | |
class Holder; | |
class AttributeBase | |
{ | |
public: | |
/*! \brief Create the attribute and bind it with the component | |
* \param task The component that contains the attribute. | |
* \param name The name of the attribute. | |
*/ | |
AttributeBase(TaskContext *task, const std::string &name); | |
/*! \brief Serialize the value of the associated variable to a string. | |
* \return The value of the attribute as a string. | |
*/ | |
virtual std::string toString() = 0; | |
/*! | |
* \return The name of the attribute. | |
*/ | |
const std::string & name() const { return name_; } | |
/*! | |
* \return The eventual documanetation associated to the attribute. | |
*/ | |
const std::string & doc() const { return doc_; } | |
/*! | |
* \param doc Associates to the attribute a documentation. | |
*/ | |
void setDoc(const std::string & doc) { doc_ = doc; } | |
/*! \brief Set the value of the attribute from its litteral value and therefore of the variable bound to it. | |
* \value The litteral value of the attribute that will be converted in the underlying variable type. | |
*/ | |
virtual void setValue(const std::string &value) = 0; // get generic | |
/*! | |
* \return The type of the bound variable. | |
*/ | |
virtual const std::type_info & asSig() = 0; | |
/*! | |
* \return Pointer to the associated variable. | |
*/ | |
virtual void * value() = 0; | |
/*! | |
* \return Cast the underlaying variable to \ref T type and return it. | |
*/ | |
template <class T> | |
T & as() | |
{ | |
if (typeid(T) != asSig()) | |
throw std::exception(); | |
else | |
return *reinterpret_cast<T*>(value()); | |
} | |
private: | |
std::string name_; | |
std::string doc_; | |
}; | |
/*! \brief Specialization for \ref AttributeBase, each attribute is templated on the type it contains. | |
*/ | |
template <class T> | |
class Attribute: public AttributeBase | |
{ | |
public: | |
/*! \brief Base constructor that associated the attribute at the task | |
* and the task variable at the at attribute. | |
* \param task The component that contains the attribute. | |
* \param name The name of the attribute. | |
* \param rvalue The reference to the variable associated to the attribute | |
*/ | |
Attribute(TaskContext *task, std::string name, T &rvalue) | |
: AttributeBase(task, name), value_(rvalue) | |
{ | |
} | |
/*! \brief Allows to set the attribute value from a variable of the same type of the contained one. | |
* \param value The value to be associated to the attribute | |
*/ | |
Attribute & operator = (const T &value) | |
{ | |
value_ = value; | |
return *this; | |
} | |
/*! | |
* \return the signature of the type embedded in the attribute. | |
*/ | |
const std::type_info &asSig() final | |
{ | |
return typeid(T); | |
} | |
/*! | |
* \brief Used to set the value of the attribute and thus of the undeline variable. | |
* \param value The value is read from the XML config file, so it is a string that | |
* it is automatically converted to the type of the attribute. Only basic types are | |
* allowed | |
*/ | |
void setValue(const std::string &value) final | |
{ | |
value_ = boost::lexical_cast<T>(value); | |
} | |
/*! | |
* \return a void ptr to the value variable. Can be used togheter with asSig to | |
* retreive the variable | |
*/ | |
void *value() final | |
{ | |
return & value_; | |
} | |
/*! | |
* \return serialize the value of the attribute into a string | |
*/ | |
std::string toString() final | |
{ | |
std::stringstream ss; | |
ss << value_; | |
return ss.str(); | |
} | |
private: | |
T &value_; | |
}; | |
class AttributeMemBase: public AttributeBase | |
{ | |
public: | |
virtual AttributeBase * relinkvalue(void * p) = 0; | |
AttributeMemBase(TaskContext *task, std::string name): AttributeBase(task,name){} | |
}; | |
/*! \brief Specialization for \ref AttributeBase, each attribute is templated on the type it contains. | |
*/ | |
template <class C, class T> | |
class AttributeMem: public AttributeMemBase | |
{ | |
public: | |
/*! \brief Base constructor that associated the attribute at the task | |
* and the task variable at the at attribute. | |
* \param task The component that contains the attribute. | |
* \param name The name of the attribute. | |
* \param rvalue The reference to the variable associated to the attribute | |
*/ | |
AttributeMem(TaskContext *task, std::string name, C * p, T(C::*m)) | |
: AttributeMemBase(task, name), this_(p),member_(m) | |
{} | |
/*! \brief Allows to set the attribute value from a variable of the same type of the contained one. | |
* \param value The value to be associated to the attribute | |
*/ | |
AttributeMem & operator = (const T &value) | |
{ | |
this_->*member_ = value; | |
return *this; | |
} | |
/*! | |
* \return the signature of the type embedded in the attribute. | |
*/ | |
const std::type_info &asSig() final | |
{ | |
return typeid(T); | |
} | |
/*! | |
* \brief Used to set the value of the attribute and thus of the undeline variable. | |
* \param value The value is read from the XML config file, so it is a string that | |
* it is automatically converted to the type of the attribute. Only basic types are | |
* allowed | |
*/ | |
void setValue(const std::string &value) final | |
{ | |
this_->*member_ = boost::lexical_cast<T>(value); | |
} | |
/*! | |
* \return a void ptr to the value variable. Can be used togheter with asSig to | |
* retreive the variable | |
*/ | |
void *value() final | |
{ | |
return & (this_->*member_); | |
} | |
/*! | |
* \return serialize the value of the attribute into a string | |
*/ | |
std::string toString() final | |
{ | |
std::stringstream ss; | |
ss << this_->*member_; | |
return ss.str(); | |
} | |
AttributeBase * relinkvalue(void * p) | |
{ | |
C * pp = (C*)p; | |
return new Attribute<T>(pp,name(),(pp->*member_)); | |
} | |
C * this_; | |
T(C::*member_); | |
}; | |
/*! \brief Allows the automatical creation of an attribute containing a vector of dynamic size. | |
* | |
*/ | |
template <class Q> | |
class Attribute<std::vector<Q> >: public AttributeBase | |
{ | |
public: | |
using T = std::vector<Q>; | |
/*! \brief Base constructor that associated the attribute at the task | |
* and the task variable at the at attribute. | |
* \param task The component that contains the attribute. | |
* \param name The name of the attribute. | |
* \param rvalue The reference to the variable (vector) associated to the attribute | |
*/ | |
Attribute(TaskContext *task, std::string name, T &rvalue) | |
: AttributeBase(task, name), value_(rvalue) | |
{} | |
/*! \brief Allows to set the attribute value from a variable of the same type of the contained one. | |
* \param value The value to be associated to the attribute. | |
*/ | |
Attribute &operator =(const T &x) | |
{ | |
value_ = x; | |
return *this; | |
} | |
/*! | |
* \return the signature of the type embedded in the attribute. | |
*/ | |
const std::type_info &asSig() final | |
{ | |
return typeid(T); | |
} | |
/*! \brief Set the valus of the vector. The supported format is CSV, with or without spaces. | |
* \param value A CSV line containing the values to be associated to the vector. | |
*/ | |
void setValue(const std::string &value) final | |
{ | |
std::string new_value = value; | |
auto pos = std::find(new_value.begin(), new_value.end(), ' '); | |
while (pos != new_value.end()) | |
{ | |
new_value.erase(pos); | |
pos = std::find(new_value.begin(), new_value.end(), ' '); | |
} | |
std::vector<Q> nv; | |
value_ = nv; | |
} | |
/*! | |
* \return a void ptr to the value variable. Can be used togheter with asSig to | |
* retreive the variable | |
*/ | |
void *value() final | |
{ | |
return &value_; | |
} | |
/*! | |
* \return serialize the value of the attribute into a string | |
*/ | |
std::string toString() final | |
{ | |
std::stringstream ss; | |
for (unsigned int i = 0; i < value_.size(); i++) | |
{ | |
if (i > 0) | |
ss << ','; | |
ss << value_[i]; | |
} | |
return ss.str(); | |
} | |
private: | |
T &value_; | |
}; | |
class Holder | |
{ | |
public: | |
/*! | |
* \param name The name of an attribute. | |
* \return Pointer to the attribute with that name. Null if no attribute with name exists. | |
*/ | |
std::shared_ptr<AttributeBase> attribute(const std::string &name); | |
template <class T> | |
std::function<T&()> attributeAccessor(const std::string & name) | |
{ | |
auto it = attributes_.find(name); | |
if (it != attributes_.end()) | |
return std::bind(&AttributeBase::as<T>, it->second.get()); | |
else | |
return std::function<T&()>(); | |
} | |
/*! | |
* \param name The name of an attribute. | |
* \return Reference to the attribute with that name. Raise exception if no attribute with name exists. | |
*/ | |
template <class T> | |
T & attributeRef(std::string name) | |
{ | |
auto it = attributes_.find(name); | |
if (it != attributes_.end()) | |
{ | |
std::cout << "found " << name <<std::endl; | |
return it->second->as<T>(); | |
} | |
else | |
{ | |
std::cout << "not found " << name << " in sized " << attributes_.size() << std::endl; | |
throw std::exception(); | |
} | |
} | |
bool addAttribute(std::shared_ptr<AttributeBase> &attribute) | |
{ | |
std::cout << "Adding " << attribute->name() << std::endl; | |
attributes_[attribute->name()] = attribute; | |
std::cout << "Added " << attribute->name() << std::endl; | |
} | |
/*! Allows to iterate over all the attributes. | |
* \return The container of the attributes. | |
*/ | |
const std::unordered_map<std::string, std::shared_ptr<AttributeBase> > & attributes() const { return attributes_; } | |
std::unordered_map<std::string, std::shared_ptr<AttributeBase> > attributes_; | |
template <class Me> | |
void populate(Me*); | |
}; | |
class MetaBase | |
{ | |
public: | |
template <class C, class T> | |
void bindProperty(const char * name, T(C::*m)) | |
{ | |
std::cout << "Binding " << name << std::endl; | |
// use same class as holder but it could be something different | |
vars_[name] = std::shared_ptr<AttributeMemBase>(new AttributeMem<C,T>(0,name,0,m)); | |
std::cout << "Bound " << name << std::endl; | |
} | |
template <class C, class R, class ...Args> | |
void bindOperation(const char * name, R(C::*m)(Args...)) | |
{ | |
} | |
std::unordered_map<std::string,std::shared_ptr<AttributeMemBase> > vars_; | |
}; | |
template <class Me> | |
void Holder::populate(Me *pthis) | |
{ | |
typename Me::Meta m; // assume this allocated somewhere | |
for(auto & q: m.vars_) | |
{ | |
q.second->relinkvalue(pthis); // auto reg to pthis | |
} | |
} | |
struct Populator | |
{ | |
public: | |
template <class T> | |
Populator(T * p) | |
{ | |
p->template populate<T>(p); | |
} | |
}; | |
class X: public Holder | |
{ | |
public: | |
Attribute<int> aa_ = {this, "a", q_}; | |
Populator pp_ = {this}; | |
class Meta: public MetaBase | |
{ | |
public: | |
Meta(); | |
}; | |
private: | |
void fx(int a, int b) {} | |
int q_ = 100; | |
}; | |
inline X::Meta::Meta() | |
{ | |
bindProperty("pippo",&X::q_); | |
bindOperation("fx",&X::fx); | |
}; | |
AttributeBase::AttributeBase(TaskContext *task, const std::string &name):name_(name) | |
{ | |
if(task) | |
{ | |
std::shared_ptr<AttributeBase> q(this); | |
task->addAttribute(q); | |
} | |
} | |
class Xwrap | |
{ | |
public: | |
Xwrap(Holder * p) : p_(p), q_(p->attributeRef<int>("pippo")) | |
{ | |
// manual wrap | |
} | |
int q() const { return q_; } | |
int & q_; | |
Holder * p_; | |
}; | |
int main(int argc, char const *argv[]) | |
{ | |
X q; | |
std::cout << q.attributeRef<int>("pippo") << std::endl; | |
std::cout << q.attributeAccessor<int>("pippo")() << std::endl; | |
Xwrap wq(&q); | |
std::cout << wq.q() << std::endl; | |
std::cout << q.attributeAccessor<int>("a")() << std::endl; | |
std::cout << "deleter bug, also present in CoCo" << std::endl; | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment