Skip to content

Instantly share code, notes, and snippets.

@eruffaldi
Created March 16, 2017 06:16
Show Gist options
  • Save eruffaldi/5070833dbf23f9f843b6859b266b7a4f to your computer and use it in GitHub Desktop.
Save eruffaldi/5070833dbf23f9f843b6859b266b7a4f to your computer and use it in GitHub Desktop.
Variable reflection using both dynamic object and member variable pointer
#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