map strings to class members - c++

Say I have a class / struct like below:
struct A {
uint32_t a;
uint8_t b;
uint16_t c;
};
And I have a set of strings that associate to each member of A (could be of different integer types, but not non-integer types such as strings), e.g.
"field1" -> A::a
"field2" -> A::b
"field3" -> A::c
Assume that there is always a 1:1 mapping between the strings and members. Is there an elegant way to map each string to each member using something like std::unordered_map?
I want to be able to read and write to each field using the strings as keys, e.g.
A a {1,2,3};
mymap["field1"] = 4; // a.a = 4
mymap["field2"] = 5; // a.b = 5
auto c = mymap["field3"]; // c = a.c = 3
I'm using C++11/14 and can't use boost.
Some more information on the context of this question:
The struct A mentioned in the question are settings of the program. They are hardware configuration parameters and my software program is used to simulate hardware behaviours. These settings/configurations are script-generated structs like above. We read / write these struct members and because the amount of these settings/configurations are so many (a few thousands), it would be convenient to be able to access them by their names as well. This is how & why I want to associate each member with a string. Whether it is subscripting or a function to access the corresponding members does not really matter, but there is such a 1:1 mapping between a string (name of the setting) and the generated struct member. And as I mentioned in the question, these members are of different integer types.

You could make a proxy-class that wraps around a integer, then store this proxy class in a std::unordered_map.
#include <iostream>
#include <functional>
#include <unordered_map>
struct A {
uint32_t a;
uint8_t b;
uint16_t c;
};
struct ValueWrapper {
using value_type = uint64_t;
template <typename Obj, typename T>
ValueWrapper(Obj& obj, T Obj::*member) {
get = [&, member]() { return obj.*member; };
set = [&, member](value_type value) mutable { obj.*member = value; };
}
ValueWrapper() = default;
ValueWrapper& operator=(value_type value) {
set(value);
return *this;
}
operator value_type() {
return get();
}
std::function<value_type()> get;
std::function<void(value_type)> set;
};
std::unordered_map<std::string, ValueWrapper> make_map(A& a) {
std::unordered_map<std::string, ValueWrapper> map;
map["field1"] = ValueWrapper(a, &A::a);
map["field2"] = ValueWrapper(a, &A::b);
map["field3"] = ValueWrapper(a, &A::c);
return map;
}
int main() {
A a{1,2,3};
auto map = make_map(a);
map["field2"] = 67;
std::cout << a.a << " " << static_cast<int>(a.b) << " " << a.c << std::endl;
std::cout << map["field1"] << " " << map["field2"] << " " << map["field3"] << std::endl;
}
You do get some restrictions depending on the value_type. If you use int64_t you could wrap anything but a uint64_t safely. If you go for a uint64_t you could wrap all the unsigned integers, but not the signed ones safely.
I put the default constructor there to satisfy unordered_maps use of operator[].

template<class V>
struct pseudo_ref_t {
operator V()&& { return getter(); }
void operator=(V v)&&{
setter(std::move(v));
}
std::function<void(V)> setter;
std::function<V()> getter;
};
template<class T, class V>
struct member_t {
friend pseudo_ref_t<V> operator->*( T* t, member_t const& self ) {
return {
[&self, t](V in){ self.setter(*t, std::move(in)); },
[&self, t]()->V{ return self.getter(*t); }
};
}
friend V operator->*( T const* t, member_t const& self ) {
return self.getter(*t);
}
std::function<void(T&, V)> setter;
std::function<V(T const&)> getter;
};
template<class T, class V, class X>
member_t<T, V> make_member( X T::* mem_ptr ) {
return {
[mem_ptr](T& t, V in) {
(t.*mem_ptr) = std::move(in);
},
[mem_ptr](T const& t)->V {
return (t.*mem_ptr);
}
};
}
a member_t<A, uint32_t> can type-erase any member of A that implicitly convertible to/from a uint32_t.
It acts like a smart member pointer.

you said that it could have different integer types, but it is a problem for having an elegant solution. If you can set up on a single type, this would be simple, like the following.
#include <iostream>
#include <map>
struct A {
int a;
int b;
int c;
};
using namespace std;
int main() {
map<string, int A::*> abc = {
{"a", &A::a},
{"b", &A::b},
{"c", &A::c}
};
A aa;
aa.*abc["a"] = 1;
aa.*abc["b"] = 2;
aa.*abc["c"] = 3;
cout << "a = " << aa.a << "(" << aa.*abc["a"] << ")" << endl;
cout << "b = " << aa.b << "(" << aa.*abc["b"] << ")" << endl;
cout << "c = " << aa.c << "(" << aa.*abc["c"] << ")" << endl;
return 0;
}

The main challenge of your question is that different members have different types.
If you make the types the same, you can use a lot of tricks.
For your case, I know you don't want to use Boost or C++17, but just to show you the challenges ahead, let me give you a Boost.Hana, C++17 solution.
#define BOOST_HANA_CONFIG_ENABLE_STRING_UDL
#include <boost/hana/equal.hpp>
#include <boost/hana/string.hpp>
#include <cstdint> // uint
#include <cassert>
struct A {
uint32_t a;
uint8_t b;
uint16_t c;
};
struct map{
A& aa_;
template<class String>
decltype(auto) operator[](String s) const{
using namespace boost::hana::literals;
if constexpr(s == "field1"_s) return (decltype(aa_.a)&)(aa_.a);
if constexpr(s == "field2"_s) return (decltype(aa_.b)&)(aa_.b);
if constexpr(s == "field3"_s) return (decltype(aa_.c)&)(aa_.c);
}
};
using namespace boost::hana::literals;
void f(uint32_t& a){ a = 3.;}
int main(){
A aa{1,2,3};
map mymap{aa};
mymap["field1"_s] = 4; assert(aa.a == 4);
mymap["field2"_s] = 5; assert(aa.b == 5);
mymap["field3"_s] = 6; assert(aa.c == 6);
auto c = mymap["field3"_s]; assert( c == aa.c );
mymap["blabla"_s]; // is void (not a compile error)
assert( map{aa}["field1"_s] == 4 );
}
From this you can walk backwards and perhaps figure out a C++14, the challenge ahead is that you have to implement your own compile time string literals and equality.
In other words, reimplement your own Hana string strings: https://www.boost.org/doc/libs/1_61_0/libs/hana/doc/html/namespaceboost_1_1hana_1_1literals.html

The simple solution is that if you have a script that can generate the struct then you can have your script generate your setters.
Start with a simple function interface.
struct A {
uint32_t a;
uint8_t b;
uint16_t c;
};
typedef std::function<void(A &,string)> SetterType
Then create a lookup table
std::map<std::string,SetterType> Lookup;
and for each field, using your script, generate a parser and setter;
void A_a(A & data, std::string input){
data.a = std::stoi(input);
}
and then
Lookup["a"] = &A_a;
and use it like
Lookup["a"]("10");
If you can't modify the generating script then maybe you can use a third party parser such as swig or clang to read your structures and generate you a parse tree that you can then use to generate your lookup tables.
Or just use a system that already maps strings to C++. A C++ JSON generator.
https://nlohmann.github.io/json/

Related

How to return a member that is defined in all alternatives of a std::variant, even if the members are different types?

Is it possible to get a variant's member called x even if it has a different type in each alternative?
#include <variant>
class A {
int x = 0;
};
class B {
int x = 1;
};
class C {
bool x = false;
};
std::variant<A, B, C> variant = A{};
template<typename R> R getValue(std::variant<A, B, C>& variant ) {
//return the value of x here
}
On Walk over variants that share the same member and get it?, I asked the same question, but it didn't specify that I wanted to get different types of values. This answer talks about std::visit() but that does not accept a lambda that returns different types.
This is what you look for, I believe:
template<typename... Ts>
auto getValue(std::variant<Ts...>& v )
{
using R = std::common_type_t<decltype(Ts::x)...>;
return std::visit([](auto &obj){return static_cast<R>(obj.x);}, v);
}
Above code doesn't require you to state the type you want back but rather deduces it by using std::common_type of the variable x in all the different possible values in your variant.
Code: http://coliru.stacked-crooked.com/a/a1f54902ea1a9db3
One more approach, again using visit. :) One thing to realize is that visit doesn't just take a single visitor, but can take a full overload set of visitor functions, allowing different functions to handle the different values.
Thus you can flip the problem around and instead of trying to return one of the different values from your variant, you can provide handlers for the values and send the value to the proper handler.
For example, using the overloaded function-call operator in a struct:
struct A { int x = 0; };
struct B { std::string x = "2"; };
struct C { bool x = true; };
int main() {
struct Process {
void operator()(A const& a) const {
cout << "Got an A: " << a.x << '\n';
}
void operator()(B const& b) const {
cout << "Got a B: " << b.x << '\n';
}
void operator()(C const& c) const {
cout << "Got a C: " << c.x << '\n';
}
};
var = A{};
std::visit(Process{}, var);
var = B{};
std::visit(Process{}, var);
var = C{};
std::visit(Process{}, var);
}
A "modern" popular alternate syntax utilizing a bunch of c++17 features (including CTAD, variadic templates, deduction guides, inheriting from lambdas, and variadic using declarations in just a few lines) also looks nice, and is approximately equivalent to the above, though the first 2 lines defining the overloaded class could be considered library code even if they do not make full sense yet.
// probably library code
template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
struct A { int x = 0; };
struct B { std::string x = "2"; };
struct C { bool x = true; };
int main() {
// and then we have this nice, concise syntax:
auto process = [](auto & variant) {
std::visit(
overloaded{
[](A const& a) { cout << "Got an A: " << a.x << '\n'; },
[](B const& b) { cout << "Got a B: " << b.x << '\n'; },
[](C const& c) { cout << "Got a C: " << c.x << '\n'; }
},
var);
};
var = A{};
process(var);
var = B{};
process(var);
var = C{};
process(var);
}
Here's both versions together in Compiler Explorer: https://godbolt.org/z/8T91W4T9G
With the signature and template of your getValue function, you would actually need to manually decide the type of R when calling it. So in main, you would call it like:
std::cout << getValue<int>(variant); // prints 0;
And if that is what you intended, then you can simply cast the .x to type R:
template<typename R>
R getValue(std::variant<A, B, C>& v )
{
return std::visit([](auto &obj){return R(obj.x);}, v);
}
However, I highly doubt that's what you intended.

Store different types of variables in one entity in C++

I am relatively new to C++, so trying to find the best way to store different types of values under one entity.
Context is, I have a function which returns a struct and one of the values can be of different types. At any time, only one value will be set.
Best case for me would be if I can do something like this:
Union Union1 {
int x;
char y;
double z;
}
executor(Union1 union1) {
if(union1.x.isSet()) {
doSomethingUsingX();
} else if(union1.y.isSet()) {
doSomethingElseUsingY();
}
}
I dont think this is possible because c++ unions use same memory space.
So is there something I can use?
If you use C++17 or newer, try std::variant. See https://en.cppreference.com/w/cpp/utility/variant
If older than C++17 you can try boost::variant
The variant is a better way to do a union. It also records what kind of thing is stored in it, and you can use std::visit to call a type overloaded function on a variant.
Like this, adapted from the cppreference example:
#include <iostream>
#include <variant>
#include <vector>
// helper type for the visitor #4
template <class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
// explicit deduction guide (not needed as of C++20)
template <class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
int main() {
std::vector<std::variant<int, char, double>> v;
v.emplace_back(1);
v.emplace_back('c');
v.emplace_back(2.7);
for (const auto &x : v) {
std::visit(
overloaded{[](auto arg) { std::cout << "unknown " << arg << '\n'; },
[](int arg) { std::cout << "int " << arg << '\n'; },
[](double arg) { std::cout << "double " << arg << '\n'; },
[](char arg) { std::cout << "char " << arg << '\n'; }},
x);
}
return 0;
}
Also this blog post I found online seems to help explain how that works:
https://pabloariasal.github.io/2018/06/26/std-variant/
You could use an enum to report the return type:
typedef enum
{
x,
y,
z
}returnType;
typedef struct
{
returnType rt;
union
{
int x;
char y;
double z;
};
}myStruct;
//...
myStruct AFunctionWithMyStructReturnType(int arg)
{
myStruct ms;
if(arg == 0)
{
ms.rt = returnType.x;
ms.val = 10000;
}
else if(arg == 1)
{
ms.rt = returnType.y;
ms.val = 200;
}
else
{
ms.rt = returnType.z;
ms.val = 3.14;
}
return ms;
}
//...
myStruct S = AFunctionWithMyStructReturnType(arg);
switch(S.rt)
{
case returnType.x:
processX(S.val);
break;
case returnType.y:
processY(S.val);
break;
case returnType.z:
processZ(S.val);
break;
}

Is there a way to declare multiple objects of the same type at once and initialize them immediately with the same rvalue by just one expression?

I want to declare multiple objects of the same type and initialize them with the same rvalue by just one expression; without the need to declare and initialize them by separate statements.
What I want is something like:
int a = b = 10; // dummy-statement. This does not work.
or
int a = int b = 10; // dummy-statement. This does not work either.
instead of
int b = 10;
int a = b;
Is there a way to do that?
Technically, yes: int a = value, b = a;, or you might consider int a, b = a = value;. Without repeating identifiers, no, at least not in C; the grammar simply does not provide for it. Each ”declarator = initializer” in the grammar can declare only one object, per grammar production in C 2018 6.7.6 1 and explicit statement in 6.7.6 2: “Each declarator declares one identifier…”
As mentioned in a comment, here is a horrible way to do it in C++. I make no representations regarding C++’s rules about order of initialization in a single declaration, issues about threading, and so on. This is presented as an educational exercise only. Never use this in production code.
template<class T> class Sticky
{
private:
static T LastInitializer; // To remember last explicit initializer.
T Value; // Actual value of this object.
public:
// Construct from explicit initializer.
Sticky<T>(T InitialValue) : Value(InitialValue)
{ LastInitializer = InitialValue; }
// Construct without initializer.
Sticky<T>() : Value(LastInitializer) {}
// Act as a T by returning const and non-const references to the value.
operator const T &() const { return this->Value; }
operator T &() { return this->Value; }
};
template<class T> T Sticky<T>::LastInitializer;
#include <iostream>
int main(void)
{
Sticky<int> a = 3, b, c = 15, d;
std::cout << "a = " << a << ".\n";
std::cout << "b = " << b << ".\n";
std::cout << "c = " << c << ".\n";
std::cout << "d = " << d << ".\n";
b = 4;
std::cout << "b = " << b << ".\n";
std::cout << "a = " << a << ".\n";
}
Output:
a = 3.
b = 3.
c = 15.
d = 15.
b = 4.
a = 3.
I think you want this:
int b=10, a=b;
Technically you can initialize several variables using structured binding available in C++17, but this is clearly a perversion:
online compiler
#include <cstddef>
#include <type_traits>
#include <tuple>
#include <utility>
// tuple-like type
template
<
::std::size_t x_count
, typename x_Value
> class
t_Pack
{
private: x_Value && m_value;
public: constexpr t_Pack(x_Value && value): m_value{::std::move(value)}
{}
public: template
<
::std::size_t x_index
> constexpr auto
get(void) noexcept -> x_Value &&
{
return ::std::move(m_value);
}
};
// specializations to make structured bindings work
namespace std
{
template
<
::std::size_t x_count
, typename x_Value
> struct
tuple_size<::t_Pack<x_count, x_Value>>
: public ::std::integral_constant<::std::size_t, x_count>
{};
template
<
::std::size_t x_index
, ::std::size_t x_count
, typename x_Value
> struct
tuple_element<x_index, ::t_Pack<x_count, x_Value>>
{
public: using type = x_Value;
};
}
// helper
template
<
::std::size_t x_count
, typename x_Value
> constexpr auto
pack(x_Value && value)
{
return t_Pack<x_count, x_Value>{::std::move(value)};
}
auto [a, b, c, d, e] = pack<5>(10);
can't do initialization.
can do assignment.
int a, b;
a = b = 10;

C++ type casting pointers to constant variables

I'm hoping there's a way to write a single get function for a class with a large number of accessible (but non-editable) data members, of mixed type. Use of a map holding void*-cast copies of the members' addresses will work, as seen in the following code, but as soon as a 'const' is thrown in to the mix to enforce read-only, unsurprisingly C++ barks saying that 'const void*' type cannot be recast in order to appropriately access the data member. The following code works for writing a single get function for a class of mixed data types, but it effectively makes all data members accessed by the get function public (see specifically the get function in the memlist class).
Bottom line:
Is there a way to make a pointer type-castable while retaining read-only at the actual memory location? Or more fundamentally, can one define a type cast-able pointer to a constant variable? E.g., it seems to me that const type *var defines a read-only/non-castable address to a read-only variable, whereas I am trying to find something (that hasn't worked for me as of yet) more like type * const var, though I haven't been able to find any documentation on this.
#include <iostream>
#include <string>
#include <map>
class A{
public:
A(int a, double b): a(a), b(b) {};
private:
int a;
double b;
friend std::ostream& operator<<(std::ostream& os, A& rhs);
};
class memlist{
public:
memlist(int param1, double param2)
{
myint = new int(param1);
mydouble = new double(param2);
myclass = new A(param1,param2);
getMap["myint"] = myint;
getMap["mydouble"] = mydouble;
getMap["myclass"] = myclass;
}
~memlist()
{
delete myint;
delete mydouble;
delete myclass;
}
void* get(std::string param) {return getMap[param];};
private:
int *myint;
double *mydouble;
A *myclass;
std::map<std::string,void*> getMap;
};
std::ostream& operator<<(std::ostream& os, A& rhs){
os << rhs.a << std::endl << rhs.b;
return os;
};
int main(){
int myint = 5;
double mydbl = 3.14159263;
memlist mymem(myint,mydbl);
std::cout << *(int*)mymem.get("myint") << std::endl;
std::cout << *(double*)mymem.get("mydouble") << std::endl;
std::cout << *(A*)mymem.get("myclass") << std::endl;
*(int*)mymem.get("myint") = 10;
std::cout << *(int*)mymem.get("myint") << std::endl;
return 0;
}
Output:
5
3.14159
5
3.14159
10
The code shown is very, shall we say, ill-designed.
void* is as close to destroying the type system as it gets in C++. As mentioned in the comments, std::any is a better solution to this.
That said, I took it as a challenge to implement what you have illustrated in the question in a type-safe manner. It was overkill, to say the least.
#include <iostream>
#include <type_traits>
using namespace std;
template<typename>
struct is_str_literal : false_type {};
template<size_t N>
struct is_str_literal<const char[N]> : true_type {};
template<typename T>
struct is_str_literal<T&> : is_str_literal<T> {};
template<typename T>
constexpr bool is_str_literal_v = is_str_literal<T>::value;
constexpr bool samestr(const char* arr1, const char* arr2, size_t n)
{
return n == 0 ? arr1[0] == arr2[0] :
(arr1[n] == arr2[n]) && samestr(arr1, arr2, n - 1);
}
template<size_t N1, size_t N2>
constexpr bool samestr(const char (&arr1)[N1], const char (&arr2)[N2])
{
return N1 == N2 ? samestr(arr1, arr2, N1 - 1) : false;
}
constexpr char myint[] = "myint";
constexpr char mydouble[] = "mydouble";
constexpr char myclass[] = "myclass";
struct S
{
template<const auto& name>
const auto& get()
{
static_assert(is_str_literal_v<decltype(name)>, "usage: get<var name>()");
if constexpr(samestr(name, ::myint))
return myint;
if constexpr(samestr(name, ::mydouble))
return mydouble;
if constexpr(samestr(name, ::myclass))
return myclass;
}
int myint;
double mydouble;
char myclass;
};
int main()
{
S s;
s.myint = 42;
s.mydouble = 10.0;
s.myclass = 'c';
cout << s.get<myint>() << endl;
cout << s.get<mydouble>() << endl;
cout << s.get<myclass>() << endl;
}
Live
This uses C++17.
After some further poking around, I have to respectfully disagree with the previous assessments in the comments and answers... I have, since posting this question, come across many functions in the standard C library where void * types are readily used (http://www.cplusplus.com/reference/cstdlib/qsort/), not to mention it being the return type of malloc (probably the most widely-used function in C/C++?) which relies on programmer type-casting. Also, to the best of my knowledge, std::any is a new c++17 class, so how might you have answered this question 6 months ago?

Changing VTBL of existing object "on the fly", dynamic subclassing

Consider the following setup.
Base class:
class Thing {
int f1;
int f2;
Thing(NO_INIT) {}
Thing(int n1 = 0, int n2 = 0): f1(n1),f2(n2) {}
virtual ~Thing() {}
virtual void doAction1() {}
virtual const char* type_name() { return "Thing"; }
}
And derived classes that are different only by implementation of methods above:
class Summator {
Summator(NO_INIT):Thing(NO_INIT) {}
virtual void doAction1() override { f1 += f2; }
virtual const char* type_name() override { return "Summator"; }
}
class Substractor {
Substractor(NO_INIT):Thing(NO_INIT) {}
virtual void doAction1() override { f1 -= f2; }
virtual const char* type_name() override { return "Substractor"; }
}
The task I have requires ability to change class (VTBL in this case) of existing objects on the fly. This is known as dynamic subclassing if I am not mistaken.
So I came up with the following function:
// marker used in inplace CTORs
struct NO_INIT {};
template <typename TO_T>
inline TO_T* turn_thing_to(Thing* p)
{
return ::new(p) TO_T(NO_INIT());
}
that does just that - it uses inplace new to construct one object in place of another. Effectively this just changes vtbl pointer in objects. So this code works as expected:
Thing* thing = new Thing();
cout << thing->type_name() << endl; // "Thing"
turn_thing_to<Summator>(thing);
cout << thing->type_name() << endl; // "Summator"
turn_thing_to<Substractor>(thing);
cout << thing->type_name() << endl; // "Substractor"
The only major problems I have with this approach is that
a) each derived classes shall have special constructors like Thing(NO_INIT) {} that shall do precisely nothing. And b) if I will want to add members like std::string to the Thing they will not work - only types that have NO_INIT constructors by themselves are allowed as members of the Thing.
Question: is there a better solution for such dynamic subclassing that solves 'a' and 'b' problems ? I have a feeling that std::move semantic may help to solve 'b' somehow but not sure.
Here is the ideone of the code.
(Already answered at RSDN http://rsdn.ru/forum/cpp/5437990.1)
There is a tricky way:
struct Base
{
int x, y, z;
Base(int i) : x(i), y(i+i), z(i*i) {}
virtual void whoami() { printf("%p base %d %d %d\n", this, x, y, z); }
};
struct Derived : Base
{
Derived(Base&& b) : Base(b) {}
virtual void whoami() { printf("%p derived %d %d %d\n", this, x, y, z); }
};
int main()
{
Base b(3);
Base* p = &b;
b.whoami();
p->whoami();
assert(sizeof(Base)==sizeof(Derived));
Base t(std::move(b));
Derived* d = new(&b)Derived(std::move(t));
printf("-----\n");
b.whoami(); // the compiler still believes it is Base, and calls Base::whoami
p->whoami(); // here it calls virtual function, that is, Derived::whoami
d->whoami();
};
Of course, it's UB.
For your code, I'm not 100% sure it's valid according to the standard.
I think the usage of the placement new which doesn't initialize any member variables, so to preserve previous class state, is undefined behavior in C++. Imagine there is a debug placement new which will initialize all uninitialized member variable into 0xCC.
union is a better solution in this case. However, it does seem that you are implementing the strategy pattern. If so, please use the strategy pattern, which will make code a lot easier to understand & maintain.
Note: the virtual should be removed when using union.
Adding it is ill-formed as mentioned by Mehrdad, because introducing virtual function doesn't meet standard layout.
example
#include <iostream>
#include <string>
using namespace std;
class Thing {
int a;
public:
Thing(int v = 0): a (v) {}
const char * type_name(){ return "Thing"; }
int value() { return a; }
};
class OtherThing : public Thing {
public:
OtherThing(int v): Thing(v) {}
const char * type_name() { return "Other Thing"; }
};
union Something {
Something(int v) : t(v) {}
Thing t;
OtherThing ot;
};
int main() {
Something sth{42};
std::cout << sth.t.type_name() << "\n";
std::cout << sth.t.value() << "\n";
std::cout << sth.ot.type_name() << "\n";
std::cout << sth.ot.value() << "\n";
return 0;
}
As mentioned in the standard:
In a union, at most one of the non-static data members can be active at any time, that is, the value of at most one of the non-static data members can be stored in a union at any time. [ Note: One special guarantee is made in order to simplify the use of unions: If a standard-layout union contains several standard-layout structs that share a common initial sequence (9.2), and if an object of this standard-layout union type contains one of the standard-layout structs, it is permitted to inspect the common initial sequence of any of standard-layout struct members; see 9.2. — end note ]
Question: is there a better solution for such dynamic subclassing that solves 'a' and 'b' problems ?
If you have fixed set of sub-classes then you may consider using algebraic data type like boost::variant. Store shared data separately and place all varying parts into variant.
Properties of this approach:
naturally works with fixed set of "sub-classes". (though, some kind of type-erased class can be placed into variant and set would become open)
dispatch is done via switch on small integral tag. Sizeof tag can be minimized to one char. If your "sub-classes" are empty - then there will be small additional overhead (depends on alignment), because boost::variant does not perform empty-base-optimization.
"Sub-classes" can have arbitrary internal data. Such data from different "sub-classes" will be placed in one aligned_storage.
You can make bunch of operations with "sub-class" using only one dispatch per batch, while in general case with virtual or indirect calls dispatch will be per-call. Also, calling method from inside "sub-class" will not have indirection, while with virtual calls you should play with final keyword to try to achieve this.
self to base shared data should be passed explicitly.
Ok, here is proof-of-concept:
struct ThingData
{
int f1;
int f2;
};
struct Summator
{
void doAction1(ThingData &self) { self.f1 += self.f2; }
const char* type_name() { return "Summator"; }
};
struct Substractor
{
void doAction1(ThingData &self) { self.f1 -= self.f2; }
const char* type_name() { return "Substractor"; }
};
using Thing = SubVariant<ThingData, Summator, Substractor>;
int main()
{
auto test = [](auto &self, auto &sub)
{
sub.doAction1(self);
cout << sub.type_name() << " " << self.f1 << " " << self.f2 << endl;
};
Thing x = {{5, 7}, Summator{}};
apply(test, x);
x.sub = Substractor{};
apply(test, x);
cout << "size: " << sizeof(x.sub) << endl;
}
Output is:
Summator 12 7
Substractor 5 7
size: 2
LIVE DEMO on Coliru
Full Code (it uses some C++14 features, but can be mechanically converted into C++11):
#define BOOST_VARIANT_MINIMIZE_SIZE
#include <boost/variant.hpp>
#include <type_traits>
#include <functional>
#include <iostream>
#include <utility>
using namespace std;
/****************************************************************/
// Boost.Variant requires result_type:
template<typename T, typename F>
struct ResultType
{
mutable F f;
using result_type = T;
template<typename ...Args> T operator()(Args&& ...args) const
{
return f(forward<Args>(args)...);
}
};
template<typename T, typename F>
auto make_result_type(F &&f)
{
return ResultType<T, typename decay<F>::type>{forward<F>(f)};
}
/****************************************************************/
// Proof-of-Concept
template<typename Base, typename ...Ts>
struct SubVariant
{
Base shared_data;
boost::variant<Ts...> sub;
template<typename Visitor>
friend auto apply(Visitor visitor, SubVariant &operand)
{
using result_type = typename common_type
<
decltype( visitor(shared_data, declval<Ts&>()) )...
>::type;
return boost::apply_visitor(make_result_type<result_type>([&](auto &x)
{
return visitor(operand.shared_data, x);
}), operand.sub);
}
};
/****************************************************************/
// Demo:
struct ThingData
{
int f1;
int f2;
};
struct Summator
{
void doAction1(ThingData &self) { self.f1 += self.f2; }
const char* type_name() { return "Summator"; }
};
struct Substractor
{
void doAction1(ThingData &self) { self.f1 -= self.f2; }
const char* type_name() { return "Substractor"; }
};
using Thing = SubVariant<ThingData, Summator, Substractor>;
int main()
{
auto test = [](auto &self, auto &sub)
{
sub.doAction1(self);
cout << sub.type_name() << " " << self.f1 << " " << self.f2 << endl;
};
Thing x = {{5, 7}, Summator{}};
apply(test, x);
x.sub = Substractor{};
apply(test, x);
cout << "size: " << sizeof(x.sub) << endl;
}
use return new(p) static_cast<TO_T&&>(*p);
Here is a good resource regarding move semantics: What are move semantics?
You simply can't legally "change" the class of an object in C++.
However if you mention why you need this, we might be able to suggest alternatives. I can think of these:
Do v-tables "manually". In other words, each object of a given class should have a pointer to a table of function pointers that describes the behavior of the class. To modify the behavior of this class of objects, you modify the function pointers. Pretty painful, but that's the whole point of v-tables: to abstract this away from you.
Use discriminated unions (variant, etc.) to nest objects of potentially different types inside the same kind of object. I'm not sure if this is the right approach for you though.
Do something implementation-specific. You can probably find the v-table formats online for whatever implementation you're using, but you're stepping into the realm of undefined behavior here so you're playing with fire. And it most likely won't work on another compiler.
You should be able to reuse data by separating it from your Thing class. Something like this:
template <class TData, class TBehaviourBase>
class StateStorageable {
struct StateStorage {
typedef typename std::aligned_storage<sizeof(TData), alignof(TData)>::type DataStorage;
DataStorage data_storage;
typedef typename std::aligned_storage<sizeof(TBehaviourBase), alignof(TBehaviourBase)>::type BehaviourStorage;
BehaviourStorage behaviour_storage;
static constexpr TData *data(TBehaviourBase * behaviour) {
return reinterpret_cast<TData *>(
reinterpret_cast<char *>(behaviour) -
(offsetof(StateStorage, behaviour_storage) -
offsetof(StateStorage, data_storage)));
}
};
public:
template <class ...Args>
static TBehaviourBase * create(Args&&... args) {
auto storage = ::new StateStorage;
::new(&storage->data_storage) TData(std::forward<Args>(args)...);
return ::new(&storage->behaviour_storage) TBehaviourBase;
}
static void destroy(TBehaviourBase * behaviour) {
auto storage = reinterpret_cast<StateStorage *>(
reinterpret_cast<char *>(behaviour) -
offsetof(StateStorage, behaviour_storage));
::delete storage;
}
protected:
StateStorageable() = default;
inline TData *data() {
return StateStorage::data(static_cast<TBehaviourBase *>(this));
}
};
struct Data {
int a;
};
class Thing : public StateStorageable<Data, Thing> {
public:
virtual const char * type_name(){ return "Thing"; }
virtual int value() { return data()->a; }
};
Data is guaranteed to be leaved intact when you change Thing to other type and offsets should be calculated at compile-time so performance shouldn't be affected.
With a propert set of static_assert's you should be able to ensure that all offsets are correct and there is enough storage for holding your types. Now you only need to change the way you create and destroy your Things.
int main() {
Thing * thing = Thing::create(Data{42});
std::cout << thing->type_name() << "\n";
std::cout << thing->value() << "\n";
turn_thing_to<OtherThing>(thing);
std::cout << thing->type_name() << "\n";
std::cout << thing->value() << "\n";
Thing::destroy(thing);
return 0;
}
There is still UB because of not reassigning thing which can be fixed by using result of turn_thing_to
int main() {
...
thing = turn_thing_to<OtherThing>(thing);
...
}
Here is one more solution
While it slightly less optimal (uses intermediate storage and CPU cycles to invoke moving ctors) it does not change semantic of original task.
#include <iostream>
#include <string>
#include <memory>
using namespace std;
struct A
{
int x;
std::string y;
A(int x, std::string y) : x(x), y(y) {}
A(A&& a) : x(std::move(a.x)), y(std::move(a.y)) {}
virtual const char* who() const { return "A"; }
void show() const { std::cout << (void const*)this << " " << who() << " " << x << " [" << y << "]" << std::endl; }
};
struct B : A
{
virtual const char* who() const { return "B"; }
B(A&& a) : A(std::move(a)) {}
};
template<class TO_T>
inline TO_T* turn_A_to(A* a) {
A temp(std::move(*a));
a->~A();
return new(a) B(std::move(temp));
}
int main()
{
A* pa = new A(123, "text");
pa->show(); // 0xbfbefa58 A 123 [text]
turn_A_to<B>(pa);
pa->show(); // 0xbfbefa58 B 123 [text]
}
and its ideone.
The solution is derived from idea expressed by Nickolay Merkin below.
But he suspect UB somewhere in turn_A_to<>().
I have the same problem, and while I'm not using it, one solution I thought of is to have a single class and make the methods switches based on a "item type" number in the class. Changing type is as easy as changing the type number.
class OneClass {
int iType;
const char* Wears() {
switch ( iType ) {
case ClarkKent:
return "glasses";
case Superman:
return "cape";
}
}
}
:
:
OneClass person;
person.iType = ClarkKent;
printf( "now wearing %s\n", person.Wears() );
person.iType = Superman;
printf( "now wearing %s\n", person.Wears() );