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.
Related
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.
I have a class that reads a script and stores variables and their respective types which I reference by name (string). The class has a templatized member method named get, which fetches the variable that matches the name parameter from one of the member tables, and needs to return data of the same type as the variable type it is assigned to. To better illustrate my question, suppose I have a simplified class such as:
class Script
{
public:
template <typename T>
T get(std::string name);
private:
//data stored in associative containers such as:
// std::map<std::string, int> int_data;
// std::map<std::string, bool> bool_data;
};
In the implementation file, the member method is specialized:
template <>
int Script::get(std::string name)
{
int value = retrieve_integer(name);
return value;
}
template <>
bool Script::get(std::string name)
{
bool value = retrieve_boolean(name);
return value;
}
I would like to call the class method like this without explicitly providing the type with script.get<int>("varName"):
Script script; // loads script and reads data
bool useVsync = script.get("useVsync"); // calls the bool specific get
int screenWidth = script.get("screenWidth"); // calls the int specific get
How would I do this without causing could not deduce template parameter T errors? I know that this should be possible, because I have used libraries that allowed such syntax.
As explained in comment, you may abuse of conversion operator:
class Script
{
public:
struct ValueProxy {
operator bool() const {
return script.bool_data[name];
}
operator int() const {
return script.int_data[name];
}
ValueProxy& operator= (bool b) {script.bool_data[name] = b;}
ValueProxy& operator= (int n) {script.int_data[name] = n;}
Script &script;
std::string name;
};
ValueProxy get(std::string name) {
return ValueProxy{*this, std::move(name)};
}
private:
//data stored in associative containers such as:
std::map<std::string, int> int_data;
std::map<std::string, bool> bool_data;
};
int main() {
Script script;
script.get("myBool") = true;
script.get("MyInt") = 42;
bool b = script.get("MyBool"); // true
int i = script.get("MyInt"); // 42
int j = script.get("MyBool"); // 0
}
I am in the process of rewriting some existing code - where previously, all answer information was stored in a string array in memory. Based on the datatype, the data was transformed in various places. Below is a quick mock up of the setup I am aiming for. Essentially you have some questions - and the structure of the answers stored in the database depends on the datatype. Generally I avoid dealing with void*, and casting them to an appropriate type - but I couldn't find a better solution that would allow me to run generic code (by means of lambdas), or be specific if the datatype is known. Templated classes won't help in this case, as all the answers need to be stored in the same vector (as some arithmetic are applied to all answers based on predefined rules).
Any advice is appreciated.
#include <vector>
#include <memory>
struct AddressData
{
wchar_t Line1[50];
wchar_t Line2[50];
long CountrySeqNo;
AddressData()
{
memset(this, 0, sizeof(*this));
};
};
struct GenericData
{
wchar_t value[200];
GenericData()
{
memset(this, 0, sizeof(*this));
};
};
enum class DataType
: short
{
GENERIC,
ADDRESS
};
class AnswerBase
{
protected:
const void* const data;
const DataType dataType;
protected:
AnswerBase(const DataType datatype, const void* const _data)
: dataType(datatype), data(data)
{
if (data == nullptr)
throw std::exception("Data may not be initialized as NULL");
};
public:
/*
Some generic methods here that would apply logic by means of lambdas etc - these would be overwritten in the derived classes
*/
template<typename T> const T& GetData() { static_assert(false, "The given type is not supported"); };
template<>
const GenericData& GetData()
{
if (DataType::GENERIC != dataType)
throw std::exception("The requested type does not match the value that initialised data");
return *static_cast<const GenericData* const>(data);
};
template<>
const AddressData& GetData()
{
if (DataType::ADDRESS != dataType)
throw std::exception("The requested type does not match the value that initialised data");
return *static_cast<const AddressData* const>(data);
};
};
class AddressAnswer
: public AnswerBase
{
public:
AddressAnswer()
: AnswerBase(DataType::ADDRESS, &answer)
{
};
protected:
AddressData answer;
};
class GenericAnswer
: public AnswerBase
{
public:
GenericAnswer()
: AnswerBase(DataType::GENERIC, &answer)
{
};
protected:
GenericData answer;
};
int main()
{
std::vector<std::shared_ptr<AnswerBase>> answers;
answers.push_back(std::make_shared<GenericAnswer>());
answers.push_back(std::make_shared<AddressAnswer>());
// In some parts of code - interact with generic methods without needing to check the underlying data type
// ....
// ....
// In parts of code where we know we are dealing with a given type - like saving to a DB
auto val1 = answers[0]->GetData<GenericData>().value;
auto val2 = answers[1]->GetData<AddressData>().Line1;
// this will give a runtime failure
//auto val3 = answers[0]->GetData<AddressData>().Line1;
return 0;
}
variant is the clean way to do this. Store it in the parent.
Alternatively, provide a variant<A,B> GetData() in the parent. Now visiting is encapsulated in the variant returned. The parent stores the data.
Alternatively, provide a virtual variant<A,B> GetData() = 0. The child type returns the data, either A or B, in the variant in question.
Alternatively, write virtual A* GetA() = 0; virtual B* GetB() = 0;. Then maybe write a template method called GetData<T> such that GetData<A>() calls GetA, etc.
Alternatively, write virtual A* Get(tag_t<A>) = 0; virtual B* Get(tag_t<B>)=0;, where
template<class T>
struct tag_t {
using type=T;
constexpr tag_t(){}
};
template<class T>
constexpr tag_t<T> tag{};
is a tag used for dispatching. Now you can call the right virtual interface by doing a Get(tag<AddressData>).
In these virtual cases, the data is stored in the derived type.
Suppose you have a namespace
approvedParams {
std::string s1 = "my_string_input_1";
std::string s2 = "my_string_input_2";
}
Outside the scope of approvedParams there exists a function myfun(std::string parm1)
Is it possible to constrain myfun signature to only accept fields of type std::string from the approvedParams namespace?
That is:
myfun("my_string_input_1") will not compile.
myfun(approvedParams::s1) will compile.
I was considering an implementation with enum. However I ultimately want use approvedParams::s1 and s2 when I am parsing key-value configuration files. enum must be of an integral type. I am not interested in adding another unnecessary layer with map<int,std::string> to connect the enum integers with std::string.
The type of s1 and s2 do not carry information about the namespace they were declared in.
You could rather easily wrap a custom type though.
namespace approvedParams
{
struct keytype { std::string val; };
keytype s1 = { "my_string_input_1" };
keytype s2 = { "my_string_input_2" };
}
void approvedParams( approvedParams::keytype );
Create an enum of approved parameter values
enum approvedParams {
val1 = 0,
val2, ...
};
And just create an array with the string using these indexes
std::string[] approvedParamValues = { "my_string_input_1", ... };
Here's my take at it, reusing #DrewDormann's idea of a wrapper type but in a way that actually enforces its usage.
The idea is basically a variant of the named constructor idiom, but only with static variables instead of fully-fledged factory functions (does this pattern have a name?).
// Declaration in .h
class ApprovedParam
{
public:
static const ApprovedParam foo;
static const ApprovedParam bar;
const std::string& value() const { return m_value; }
private:
std::string m_value;
ApprovedParam(const char* value) : m_value(value) {}
ApprovedParam(std::string&& value) : m_value(std::move(value)) {}
ApprovedParam(const std::string& value) : m_value(value) {}
// IMPORTANT: the user must not be able to default construct an ApprovedParam
ApprovedParam() = delete;
};
// Definitions in .cpp
const ApprovedParam ApprovedParam::foo = "foo";
const ApprovedParam ApprovedParam::bar = "bar";
Now the user can copy/move/assign ApprovedParam objects but he has only a limited set of (immutable) instances to choose from and he will not be able to create completely new ones.
I've got an interesting problem that's cropped up in a sort of pass based compiler of mine. Each pass knows nothing of other passes, and a common object is passed down the chain as it goes, following the chain of command pattern.
The object that is being passed along is a reference to a file.
Now, during one of the stages, one might wish to associate a large chunk of data, such as that file's SHA512 hash, which requires a reasonable amount of time to compute. However, since that chunk of data is only used in that specific case, I don't want all file references to need to reserve space for that SHA512. However, I also don't want other passes to have to recalculate the SHA512 hash over and over again. For example, someone might only accept files which match a given list of SHA512s, but they don't want that value printed when the file reference gets to the end of the chain, or perhaps they want both, or... .etc.
What I need is some sort of container which contain only one of a given type. If the container does not contain that type, it needs to create an instance of that type and store it somehow. It's basically a dictionary with the type being the thing used to look things up.
Here's what I've gotten so far, the relevant bit being the FileData::Get<t> method:
class FileData;
// Cache entry interface
struct FileDataCacheEntry
{
virtual void Initalize(FileData&)
{
}
virtual ~FileDataCacheEntry()
{
}
};
// Cache itself
class FileData
{
struct Entry
{
std::size_t identifier;
FileDataCacheEntry * data;
Entry(FileDataCacheEntry *dataToStore, std::size_t id)
: data(dataToStore), identifier(id)
{
}
std::size_t GetIdentifier() const
{
return identifier;
}
void DeleteData()
{
delete data;
}
};
WindowsApi::ReferenceCounter refCount;
std::wstring fileName_;
std::vector<Entry> cache;
public:
FileData(const std::wstring& fileName) : fileName_(fileName)
{
}
~FileData()
{
if (refCount.IsLastObject())
for_each(cache.begin(), cache.end(), std::mem_fun_ref(&Entry::DeleteData));
}
const std::wstring& GetFileName() const
{
return fileName_;
}
//RELEVANT METHOD HERE
template<typename T>
T& Get()
{
std::vector<Entry>::iterator foundItem =
std::find_if(cache.begin(), cache.end(), boost::bind(
std::equal_to<std::size_t>(), boost::bind(&Entry::GetIdentifier, _1), T::TypeId));
if (foundItem == cache.end())
{
std::auto_ptr<T> newCacheEntry(new T);
Entry toInsert(newCacheEntry.get(), T::TypeId);
cache.push_back(toInsert);
newCacheEntry.release();
T& result = *static_cast<T*>(cache.back().data);
result.Initalize(*this);
return result;
}
else
{
return *static_cast<T*>(foundItem->data);
}
}
};
// Example item you'd put in cache
class FileBasicData : public FileDataCacheEntry
{
DWORD dwFileAttributes;
FILETIME ftCreationTime;
FILETIME ftLastAccessTime;
FILETIME ftLastWriteTime;
unsigned __int64 size;
public:
enum
{
TypeId = 42
}
virtual void Initialize(FileData& input)
{
// Get file attributes and friends...
}
DWORD GetAttributes() const;
bool IsArchive() const;
bool IsCompressed() const;
bool IsDevice() const;
// More methods here
};
int main()
{
// Example use
FileData fd;
FileBasicData& data = fd.Get<FileBasicData>();
// etc
}
For some reason though, this design feels wrong to me, namely because it's doing a whole bunch of things with untyped pointers. Am I severely off base here? Are there preexisting libraries (boost or otherwise) which would make this clearer/easier to understand?
As ergosys said already, std::map is the obvious solution to your problem. But I can see you concerns with RTTI (and the associated bloat). As a matter of fact, an "any" value container does not need RTTI to work. It is sufficient to provide a mapping between a type and an unique identifier. Here is a simple class that provides this mapping:
#include <stdexcept>
#include <boost/shared_ptr.hpp>
class typeinfo
{
private:
typeinfo(const typeinfo&);
void operator = (const typeinfo&);
protected:
typeinfo(){}
public:
bool operator != (const typeinfo &o) const { return this != &o; }
bool operator == (const typeinfo &o) const { return this == &o; }
template<class T>
static const typeinfo & get()
{
static struct _ti : public typeinfo {} _inst;
return _inst;
}
};
typeinfo::get<T>() returns a reference to a simple, stateless singleton which allows comparisions.
This singleton is created only for types T where typeinfo::get< T >() is issued anywhere in the program.
Now we are using this to implement a top type we call value. value is a holder for a value_box which actually contains the data:
class value_box
{
public:
// returns the typeinfo of the most derived object
virtual const typeinfo& type() const =0;
virtual ~value_box(){}
};
template<class T>
class value_box_impl : public value_box
{
private:
friend class value;
T m_val;
value_box_impl(const T &t) : m_val(t) {}
virtual const typeinfo& type() const
{
return typeinfo::get< T >();
}
};
// specialization for void.
template<>
class value_box_impl<void> : public value_box
{
private:
friend class value_box;
virtual const typeinfo& type() const
{
return typeinfo::get< void >();
}
// This is an optimization to avoid heap pressure for the
// allocation of stateless value_box_impl<void> instances:
void* operator new(size_t)
{
static value_box_impl<void> inst;
return &inst;
}
void operator delete(void* d)
{
}
};
Here's the bad_value_cast exception:
class bad_value_cast : public std::runtime_error
{
public:
bad_value_cast(const char *w="") : std::runtime_error(w) {}
};
And here's value:
class value
{
private:
boost::shared_ptr<value_box> m_value_box;
public:
// a default value contains 'void'
value() : m_value_box( new value_box_impl<void>() ) {}
// embedd an object of type T.
template<class T>
value(const T &t) : m_value_box( new value_box_impl<T>(t) ) {}
// get the typeinfo of the embedded object
const typeinfo & type() const { return m_value_box->type(); }
// convenience type to simplify overloading on return values
template<class T> struct arg{};
template<class T>
T convert(arg<T>) const
{
if (type() != typeinfo::get<T>())
throw bad_value_cast();
// this is safe now
value_box_impl<T> *impl=
static_cast<value_box_impl<T>*>(m_value_box.get());
return impl->m_val;
}
void convert(arg<void>) const
{
if (type() != typeinfo::get<void>())
throw bad_value_cast();
}
};
The convenient casting syntax:
template<class T>
T value_cast(const value &v)
{
return v.convert(value::arg<T>());
}
And that's it. Here is how it looks like:
#include <string>
#include <map>
#include <iostream>
int main()
{
std::map<std::string,value> v;
v["zero"]=0;
v["pi"]=3.14159;
v["password"]=std::string("swordfish");
std::cout << value_cast<int>(v["zero"]) << std::endl;
std::cout << value_cast<double>(v["pi"]) << std::endl;
std::cout << value_cast<std::string>(v["password"]) << std::endl;
}
The nice thing about having you own implementation of any is, that you can very easily tailor it to the features you actually need, which is quite tedious with boost::any. For example, there are few requirements on the types that value can store: they need to be copy-constructible and have a public destructor. What if all types you use have an operator<<(ostream&,T) and you want a way to print your dictionaries? Just add a to_stream method to box and overload operator<< for value and you can write:
std::cout << v["zero"] << std::endl;
std::cout << v["pi"] << std::endl;
std::cout << v["password"] << std::endl;
Here's a pastebin with the above, should compile out of the box with g++/boost: http://pastebin.com/v0nJwVLW
EDIT: Added an optimization to avoid the allocation of box_impl< void > from the heap:
http://pastebin.com/pqA5JXhA
You can create a hash or map of string to boost::any. The string key can be extracted from any::type().