Dynamic member pointer array - c++

Is it possible to create a dynamic array to save the names of each variable in a structure?
typedef struct dataSave {
uint8_t nb;
bool state;
uint16_t dataIRArray[512];
} dataSave_t;
dataSave_t mysave;
template <typename T>
struct config_t {
auto T::*var;
char* name;
};
const config_t<dataSave_t> config[] = {
{&dataSave_t::nb, "nb"},
{&dataSave_t::state, "state"},
{&dataSave_t::dataIRArray, "dataIRArray"}
};
The types of the structure's variables are different, and always generate an error:
error: non-static data member declared with placeholder 'auto'
I need to create something similar to dynamically going through a JSON and converting the JSON data just by adding it to the config list.

I guess you want something like this:
template <typename owner>
struct desc_base
{
virtual ~desc_base() = default;
virtual std::string to_string(const owner&) = 0;
virtual void from_string(owner&, std::string_view) = 0;
const std::string name;
desc_base(std::string_view name) : name(name) {}
};
template <typename owner, typename member>
struct desc : desc_base<owner>
{
member owner::*ptr;
std::string to_string(const owner& o) override { return my_to_string(o.*ptr); }
void from_string(owner& o, std::string_view s) override { my_from_string(s, o.*ptr); }
desc (member owner::*ptr, std::string_view name) : desc_base<owner>(name), ptr(ptr) {}
};
template <typename owner, typename member>
std::unique_ptr<desc<owner, member>>
make_desc(member owner::*ptr, std::string_view name) { return std::make_unique<desc<owner, member>>(ptr, name); }
const std::unique_ptr<desc_base<dataSave_t>> config[] {
make_desc(&dataSave_t::nb, "nb"),
make_desc(&dataSave_t::state, "state"),
make_desc(&dataSave_t::dataIRArray, "dataIRAArray")
};
This uses basic polymorphism technique to store an array of (smart pointers to) base class objects, where all the real work being done by the derived objects via virtual functions. Real work in this case being from_string and to_string. You can replace them with functions doing whatever real work you need to do, or just define my_to_string and my_from_string.

Related

C++ automatically choose correct overloaded function based on argument type

I'm creating a interface OutputItem<T> for writing to a json object. I want there to be different modes of writing to the json object, thus I have provided a overloaded method json_serialize which takes a SetConstructType* or AppendConstructType* argument, which are derived structs of ConstructType. Upon construction of OutputItem<T>, I want the user to pass a ConstructType* in the constructor of OutputItem<T> which will be saved to const ConstructType* constructType to be used later to automatically pick the right overloaded type when the public method void write(nlohmann::json& json) is called.
However I'm getting a error:
'void OutputItem::json_serialize(const SetConstructType *,nlohmann::json &)': cannot convert argument 1 from 'const ConstructType *' to 'const SetConstructType *'
Can someone show me the correct way of achieving this automatic overload resolution? Additionally is this a example of a SFINAE design?
Thanks
struct ConstructType {
protected:
ConstructType() {}
};
struct SetConstructType : public ConstructType {};
struct AppendConstructType : public ConstructType {};
static SetConstructType* SetConstruct;
static AppendConstructType* AppendConstruct;
template<typename T>
struct OutputItem {
OutputItem(const std::string& cgroupName, const std::string& citemName, const T& cdata, ConstructType* cconstructType)
: itemName(citemName), groupName(cgroupName), data(cdata), constructType(cconstructType) {}
const std::string itemName;
const std::string groupName;
const T data;
const ConstructType* constructType;
void write(nlohmann::json& json) {
json_serialize(constructType, json);
}
private:
void json_serialize(const SetConstructType*, nlohmann::json& json) {
json[groupName][itemName] = data;
}
void json_serialize(const AppendConstructType*, nlohmann::json& json) {
json[groupName][itemName].push_back(data);
}
};
Usage
json response;
OutputItem<int> item1("testgroup", "item1", 2, AppendConstruct);
OutputItem<int> item2("testgroup", "item2", 2, SetConstruct);
item1.write(response);
item2.write(response);
Since you can't select an overload at runtime, you need a more dynamic mechanism.
It is difficult to solve this with your external hierarchy since you can't have virtual templates.
One alternative would be to redirect through a member function pointer instead.
Illustration (without all your parameters and data):
enum class Construction
{
Set,
Append
};
template<typename T>
class OutputItem {
public:
OutputItem(Construction kind): construct(select(kind)) {}
void write() { (this->*construct)(); }
private:
using ConstructType = void (OutputItem::*)();
ConstructType construct;
ConstructType select(Construction c)
{
static ConstructType cs[] = { &OutputItem::setConstruct,
&OutputItem::appendConstruct };
return cs[static_cast<int>(c)];
}
void setConstruct() { std::cout << "Set\n"; }
void appendConstruct() { std::cout << "Append\n"; }
};
//...
int main()
{
OutputItem<int> o0(Construction::Set);
o0.write();
OutputItem<std::string> o1(Construction::Append);
o1.write();
}

Reading in each data member of an unspecified struct from XML

I have some structs with varying types and amounts of data members:
struct Foo
{
int a;
float b;
char c;
}
struct Bar
{
std::string a;
int b;
}
struct Baz
{
char a;
}
I want to fill these data members with values contained in an external file. Is there a general solution such that if I later add a new struct with different data members, I won't have to alter my code that's reading in the data? I'd prefer to keep any functionality independent of these structs, but my best idea at the moment is a polymorphic solution where each struct has a static method responsible for reading in its data.
C++ as of now doesn't have a way to reflect the content of a struct. This means that there is no truly generic way to read into any given struct (neither by name nor by index). However you might create some kind of meta-description which might result in less overhead than to rewrite the XML deserialization for each structure.
Such a meta-description could look like (requires C++17):
enum FieldType { String, Int, Float, Char };
struct AbstractField
{
FieldType const type;
const char * const name;
AbstractField(FieldType type, const char *name) : type(type), name(name) {}
virtual ~AbstractField() {}
virtual bool setValue(std::any & dest, const std::any & value) const = 0;
}
template <typename Struct, typename Type>
struct Field : public AbstractField
{
Type Struct::*const fieldPtr;
Field(FieldType type, const char *name, Type Struct::*fieldPtr)
: AbstractField(type, name), fieldPtr(fieldPtr)
{}
bool setValue(std::any & dest, const std::any & value) const override
{
Struct *realDest = std::any_cast<Struct>(& dest);
const Type *realValue = std::any_cast<Type>(& value);
if (realDest != nullptr && realValue != nullptr) {
realDest->*fieldPtr = *realValue;
return true;
}
return false;
}
}
// The following has to be redone for each structure
static const std::vector<const AbstractField *> metaFoo = {
new Field<Foo, int>(Int, "a", & Foo::a)),
new Field<Foo, float>(Float, "b", & Foo::b)),
new Field<Foo, char>(Char, "c", & Foo:c))
};
There could be added some extra logic to get this even fancier (type_id instead of the enum, templated functions that only take the name and the member pointer to create the Field instance, abstract factories for the individual structs, etc).
The reader could then set a value in the structure like this
std::any parseValue(const std::string & value, FieldType type);
bool setField(std::any &dest, const std::vector<const AbstractField*> &meta,
const std::string & fieldName, const std::string & value)
{
for (auto field : meta) {
if (field->name == fieldName) {
std::any value = parseValue(value, field->type);
return field->setValue(dest, value);
}
}
return false;
}
When combined with abstract factories for the structs (similar to what I've done for the fields) this allows to instanciate a whole tree of structures just from the meta-description.

unique container for template class

I have an algorithm (not preseted here) which takes as input different parameters (int, float, vectors).
My idea of design was to have an container which holds all these differents parameters.
To achive this, I have a base class Parameter and a derivated template class TypeParameter.
These parameters will be holded in a container.
The design is presented below:
#pragma once
#include <utility>
#include <memory>
#include <string>
#include <vector>
namespace parameter
{
/*
Interface for parameter
*/
class Parameter
{
public:
Parameter() {}
Parameter(std::string param_name) : name(param_name) {}
Parameter(const Parameter&& other) noexcept : name(std::move(other.name)) {}
virtual ~Parameter() {}
inline const std::string get_name() { return name; }
private:
std::string name;
};
/*
*/
template<class T>
class TypeParameter
: public Parameter
{
public:
TypeParameter(std::string param_name, T new_value) : Parameter(param_name), value(new_value) {}
TypeParameter(const TypeParameter&& other) noexcept : Parameter(std::move(other)), value(std::move(other.T)) {}
inline const T get_value() { return value; }
private:
T value;
};
/*
Container for parameters
*/
class ParameterSet
{
public:
ParameterSet() {}
void add(std::unique_ptr<Parameter> param) { data.push_back(std::move(param)); }
private:
std::vector <std::unique_ptr<Parameter>> data;
};
} //namespace parameter
The main is:
#include <iostream>
#include <string>
#include "Parameter.h"
using parameter::TypeParameter;
using parameter::Parameter;
using parameter::ParameterSet;
void foo(std::unique_ptr<Parameter> p)
{
std::cout << p->get_value(); // ERROR
}
int main(int argc, char *argv[])
{
TypeParameter<int> *iparam = new TypeParameter<int>("ee", 3);
std::unique_ptr<Parameter> p = std::make_unique <TypeParameter<int>>("foo", 3);
foo(std::move(p));
ParameterSet param_set;
param_set.add(std::unique_ptr<Parameter>(iparam));
param_set.add(std::move(p));
getchar();
}
My problem is I cannot get the value without a cast.
Hence, my question is how do I cast the unique_ptr from a Parameter class to derived TypeParameter.
Is there another way to design the container?
Thanks a lot!
You don't have to reinvent the wheel. There are a couple of classes you can use from the standard library:
std::variant.
As suggested by the comments, variant is a type-safe union of a pre-defined set of data types, which you put in the templates argument of variant.
For example, a std::variant<int,float,double> can hold any value of type int, float, or double, but nothing else.
To use the stored value, you can either use the visitor pattern with the std::visit() function. Other functions allow you to know which of the preset types is stored in the variable (index()) and to extract the value from it (using get()). If you try to extract the value of the wrong type, the get() function throws an exception
std::any
is another utility that can hold different data types. As opposed to variant, you don't have to know the types at compile-time. Basically, it stores a void* to the data with a typeinfo to remember its original type. You can then use any_cast to cast the variable back to its original type. Just like variant, an exception is thrown when trying to cast to the wrong type.
These two classes are available in C++ 17. If these features are not available to you, they were also included in boost (respectively boost:variant and boost:any)
You can store the set of values in a standard library container, e.g. in a std::vector<std::variant<int,float,double>> or a std::vector<std::any>>.
Alternative to std::variant/std::any is the old way polymorphism:
class Parameter
{
public:
Parameter(const std::string& param_name) : name(param_name) {}
virtual ~Parameter() = default;
const std::string& get_name() const { return name; }
virtual void printValue() const = 0;
// Other virtual methods
private:
std::string name;
};
template<class T>
class TypeParameter : public Parameter
{
public:
TypeParameter(const std::string& name, const T& t) : Parameter(name), value(t) {}
// Non virtual method when we don't access it by base class.
const T& get_value() const { return value; }
void printValue() const { std::cout << value; }
private:
T value;
};
And then your
void foo(const Parameter& p)
{
std::cout << p.get_value(); // ERROR
}
becomes
void foo(const Parameter& p)
{
p.print();
}
If you don't want to add many virtual methods to Parameter, then Visitor pattern can help, but then you have to know each derived types.

Template a member variable

Consider the following two classes:
class LunchBox
{
public:
std::vector<Apple> m_apples;
};
and
class ClassRoom
{
public:
std::vector<Student> m_students;
};
The classes are alike in that they both contain a member variable vector of objects; however, they are unalike in that the vector's objects are different and the member variables have different names.
I would like to write a template that takes either LunchBox or ClassRoom as a template argument (or some other parameter) and an existing object of the same type (similar to a std::shared_ptr). The template would return an object that adds a getNthElement(int i); member function to improve accessing the methods. Usage would be like:
// lunchBox is a previously initialized LunchBox
// object with apples already pushed into m_apples
auto lunchBoxWithAccessor = MyTemplate<LunchBox>(lunchBox);
auto apple3 = lunchBoxWithAccessor.getNthElement(3);
I would like to do this without writing template specializations for each class (which likely would require specifying the member variable to operate on in some way). Preferably, I do not want to modify the LunchBox or ClassRoom classes. Is writing such a template possible?
You can minimize the amount of code that has to be written for each class -- it doesn't have to be a template specialization and it doesn't have to be an entire class.
class LunchBox
{
public:
std::vector<Apple> m_apples;
};
class ClassRoom
{
public:
std::vector<Student> m_students;
};
// you need one function per type, to provide the member name
auto& get_associated_vector( Student& s ) { return s.m_apples; }
auto& get_associated_vector( ClassRoom& r ) { return r.m_students; }
// and then the decorator is generic
template<typename T>
class accessor_decorator
{
T& peer;
public:
auto& getNthElement( int i ) { return get_associated_vector(peer).at(i); }
auto& takeRandomElement( int i ) { ... }
// many more ways to manipulate the associated vector
auto operator->() { return &peer; }
};
LunchBox lunchBox{};
accessor_decorator<LunchBox> lunchBoxWithAccessor{lunchBox};
auto apple3 = lunchBoxWithAccessor.getNthElement(3);
The simple helper function overload should ideally be in the same namespace as the type, to make argument-dependent lookup work (aka Koenig lookup).
It's also possible to specify the member at the point of construction, if you prefer to do that:
template<typename T, typename TMemberCollection>
struct accessor_decorator
{
// public to make aggregate initialization work
// can be private if constructor is written
T& peer;
TMemberCollection const member;
public:
auto& getNthElement( int i ) { return (peer.*member).at(i); }
auto& takeRandomElement( int i ) { ... }
// many more ways to manipulate the associated vector
auto operator->() { return &peer; }
};
template<typename T, typename TMemberCollection>
auto make_accessor_decorator(T& object, TMemberCollection T::*member)
-> accessor_decorator<T, decltype(member)>
{
return { object, member };
}
LunchBox lunchBox{};
auto lunchBoxWithAccessor = make_accessor_decorator(lunchBox, &LunchBox::m_apples);
auto apple3 = lunchBoxWithAccessor.getNthElement(3);
A simple way to do this is define a trait struct that has specializations with just the information that makes each case different. Then you have a template class that uses this traits type:
// Declare traits type. There is no definition though. Only specializations.
template <typename>
struct AccessorTraits;
// Specialize traits type for LunchBox.
template <>
struct AccessorTraits<LunchBox>
{
typedef Apple &reference_type;
static reference_type getNthElement(LunchBox &box, std::size_t i)
{
return box.m_apples[i];
}
};
// Specialize traits type for ClassRoom.
template <>
struct AccessorTraits<ClassRoom>
{
typedef Student &reference_type;
static reference_type getNthElement(ClassRoom &box, std::size_t i)
{
return box.m_students[i];
}
};
// Template accessor; uses traits for types and implementation.
template <typename T>
class Accessor
{
public:
Accessor(T &pv) : v(pv) { }
typename AccessorTraits<T>::reference_type getNthElement(std::size_t i) const
{
return AccessorTraits<T>::getNthElement(v, i);
}
// Consider instead:
typename AccessorTraits<T>::reference_type operator[](std::size_t i) const
{
return AccessorTraits<T>::getNthElement(v, i);
}
private:
T &v;
};
A few notes:
In this case, the implementation would technically be shorter without a traits type; with only specializations of Accessor for each type. However, the traits pattern is a good thing to learn as you now have a way to statically reflect on LunchBox and ClassRoom in other contexts. Decoupling these pieces can be useful.
It would be more idiomatic C++ to use operator[] instead of getNthElement for Accessor. Then you can directly index the accessor objects.
AccessorTraits really isn't a good name for the traits type, but I'm having trouble coming up with anything better. It's not the traits of the accessors, but the traits of the other two relevant classes -- but what concept even relates those two classes? (Perhaps SchoolRelatedContainerTraits? Seems a bit wordy...)
You said:
I would like to do this without writing template specializations for each class
I am not sure why that is a constraint. What is not clear is what else are you not allowed to use.
If you are allowed to use couple of function overloads, you can get what you want.
std::vector<Apple> const& getObjects(LunchBox const& l)
{
return l.m_apples;
}
std::vector<Student> const& getObjects(ClassRoom const& c)
{
return c.m_students;
}
You can write generic code that works with both LaunchBox and ClassRoom without writing any other specializations. However, writing function overloads is a form of specialization.
Another option will be to update LaunchBox and ClassRoom with
class LunchBox
{
public:
std::vector<Apple> m_apples;
using ContainedType = Apple;
};
class ClassRoom
{
public:
std::vector<Student> m_students;
using ContainedType = Apple;
};
and then, take advantage of the fact that
LaunchBox b;
std::vector<Apple>* ptr = reinterpret_cast<std::vector<Apple>*>(&b);
is a legal construct. Then, the following class will work fine.
template <typename Container>
struct GetElementFunctor
{
using ContainedType = typename Container::ContainedType;
GetElementFunctor(Container const& c) : c_(c) {}
ContainedType const& getNthElement(std::size_t n) const
{
return reinterpret_cast<std::vector<ContainedType> const*>(&c_)->operator[](n);
}
Container const& c_;
};
and you can use it as:
LunchBox b;
b.m_apples.push_back({});
auto f = GetElementFunctor<LunchBox>(b);
auto item = f.getNthElement(0);
I did a test case sample using a few basic classes:
class Apple {
public:
std::string color_;
};
class Student {
public:
std::string name_;
};
class LunchBox {
public:
std::vector<Apple> container_;
};
class ClassRoom {
public:
std::vector<Student> container_;
};
However for the template function that I wrote I did however have to change the name of the containers in each class to match for this to work as this is my template function:
template<class T>
auto accessor(T obj, unsigned idx) {
return obj.container_[idx];
}
And this is what my main looks like:
int main() {
LunchBox lunchBox;
Apple green, red, yellow;
green.color_ = std::string( "Green" );
red.color_ = std::string( "Red" );
yellow.color_ = std::string( "Yellow" );
lunchBox.container_.push_back(green);
lunchBox.container_.push_back(red);
lunchBox.container_.push_back(yellow);
ClassRoom classRoom;
Student s1, s2, s3;
s1.name_ = std::string("John");
s2.name_ = std::string("Sara");
s3.name_ = std::string("Mike");
classRoom.container_.push_back(s1);
classRoom.container_.push_back(s2);
classRoom.container_.push_back(s3);
for (unsigned u = 0; u < 3; u++) {
auto somethingUsefull = accessor(lunchBox, u);
std::cout << somethingUsefull.color_ << std::endl;
auto somethingElseUsefull = accessor(classRoom, u);
std::cout << somethingElseUsefull.name_ << std::endl;
}
return 0;
}
I'm not sure if there is a work around to have a different variable name from each different class this function can use; but if there is I haven't figured it out as of yet. I can continue to work on this to see if I can improve it; but this is what I have come up with so far.

distinguishing between static and non-static methods in c++ at compile time?

For some tracing automation for identifying instances i want to call either:
a non-static method of the containing object returning its identifier
something else which always returns the same id
My current solution is to have a base class with a method which() and a global function which() which should be used if not in the context of an object.
This however does not work for static member functions, here the compiler prefers the non-static method over the global one.
Simplified example:
class IdentBase
{
public:
Ident(const std::string& id) _id(id) {}
const std::string& which() const { return _id; }
private:
const std::string _id;
};
const std::string& which() { static const std::string s("bar"); return s; }
#define ident() std::cout << which() << std::endl
class Identifiable : public IdentBase
{
public:
Identifiable() : Ident("foo") {}
void works() { ident(); }
static void doesnt_work() { ident(); } // problem here
};
Can i somehow avoid using work-arounds like a special macro for static member functions (maybe using some template magic)?
Define a function template that returns a default identifier for all types.
template<typename T>
const std::string& which(const T& object)
{ static const std::string s("bar"); return s; }
Specialize the function template for the specific class.
class IdentBase
{
public:
IdentBase(const std::string& id): _id(id) {}
const std::string& id() const { return _id; }
private:
const std::string _id;
};
template<>
const std::string& which(const IdentBase& object)
{ return object.id(); }
Call the function template by passing an instance that you want to identify.
int main()
{
int i;
std::cout << which(i) << std::endl;
IdentBase foo("foo");
std::cout << which(foo) << std::endl;
return 0;
}
You might be able to to use is_member_function_pointer from the Boost TypeTraits library. sbi's suggestion of using different code in the static and non-static cases is probably better though.
Do you need a different identifier for every instance of each class as in your example, or are you just trying to identify which class is in the trace?
Changing your which() function and _id member to static would expose them both to your static member functions, and as a bonus decrease your memory usage.