Is there any problem in performing dynamic casting via the following function?
template<typename Base, typename Derived>
requires std::is_convertible_v<Derived&, Base&> &&
std::is_polymorphic_v<Base>
inline std::unique_ptr<Derived> cast_to(std::unique_ptr<Base>&& ptr)
{
return std::unique_ptr<Derived>(dynamic_cast<Derived*>(ptr.release()));
}
Yes, your function can easily leak memory if the cast fails. Consider the following:
struct Base
{
virtual ~Base() = default;
};
struct Derived1 : Base {};
struct Derived2 : Base {};
int main()
{
std::unique_ptr<Base> bp = std::make_unique<Derived1>();
// bp does not point to a Derived2, so this cast will fail
auto d2p = cast_to<Base, Derived2>(std::move(bp));
std::cout << bp.get() << '\n'; // 0
std::cout << d2p.get() << '\n'; // also 0; object was leaked
}
Demo
From this snippet you can also see another small issue: because of the order of the template parameters you have to supply them both. You can't let the compiler deduce Base because it comes before Derived.
With both of those issues in mind, the following would be a better implementation:
template<typename Derived, typename Base> // swap the order of template parameters
requires std::is_convertible_v<Derived&, Base&> &&
std::is_polymorphic_v<Base>
inline std::unique_ptr<Derived> cast_to(std::unique_ptr<Base>&& ptr)
{
Derived* d = dynamic_cast<Derived*>(ptr.get());
if (d) {
ptr.release();
return std::unique_ptr<Derived>(d);
}
return nullptr; // object is still owned by ptr
}
This fixes both of the above issues:
int main()
{
std::unique_ptr<Base> bp = std::make_unique<Derived1>();
// No need to explicitly specify Base; the compiler can deduce that itself
auto d2p = cast_to<Derived2>(std::move(bp));
std::cout << bp.get() << '\n'; // not 0; no leak
std::cout << d2p.get() << '\n'; // 0
}
Demo
Related
I am trying to use auto in combination with a virtual function. Please consider the following code:
#include <iostream>
#include <vector>
struct foo {
virtual auto get() = 0;
};
template <typename T>
struct bar : foo
{
virtual T get() override {return new T;};
};
int main() {
std::vector<foo*> Vec;
Vec.push_back(new bar<decltype(1333)>);
Vec.push_back(new bar<decltype(3.14159)>);
Vec.push_back(new bar<decltype(true)>);
std::cout << Vec[0]->get() << std::endl;
std::cout << Vec[1]->get() << std::endl;
std::cout << Vec[2]->get() << std::endl;
}
You can see online at Godbolt that code does not compile. The compiler returns:
error: virtual function cannot have deduced return type
Please help me to fix the above code.
This will not work in C++. Vec[i]->get() must imply a return type at compile time. How to do something like this depends on your needs. Assuming you have a closed set of types you want to work with, you can use std::variant:
#include <iostream>
#include <variant>
#include <vector>
struct foo {
using ReturnType = std::variant<int, bool, double>;
virtual ReturnType get() = 0;
// you need a virtual destructor in all base classes
virtual ~foo() = default;
};
template <typename T>
struct bar : public foo {
using foo::ReturnType;
ReturnType get() override {
return T{};
};
};
int main() {
std::vector<foo*> Vec;
Vec.push_back(new bar<decltype(1333)>);
Vec.push_back(new bar<decltype(3.14159)>);
Vec.push_back(new bar<decltype(true)>);
auto const visiter = [](auto const elm) { std::cout << elm << '\n'; };
for (auto& elm : Vec) {
std::visit(visiter, elm->get());
delete elm; // don't leak memory
elm = nullptr;
}
}
Don't use raw-owning pointers in this context. They will lead to memory leaks.
First, see here: C++ virtual function return type
Your problem is here:
virtual auto get() = 0;
A virtual function needs to have a known return type, or how will the overrides be checked to ensure covariance of their return types?
In particular, you seem to want get() overrides which return int, double, and bool. But these types are not covariant with anything (they have no base class), so you can't do that, because there is no type you can put in place of auto that will actually work.
You could make a base class for the value types and then store wrappers derived from that:
struct BaseValue {};
struct IntValue : BaseValue {};
// etc
But then it's hard to see how you can do anything useful with the BaseValue* you'd get().
I would like to have a unique_ptr class member that points to the base class, but later in the constructor through polymorphism can be changed to point to a sister class that also derives from the same base class.
While I don't get any errors in the constructor setting this polymorphism, it does not seem to work correctly, since I get error messages that my polymorphic pointer can't find a member of the sister class to which I thought the pointer was now pointing.
How do I correctly achieve polymorphism here?
class A {
int bar;
};
class B : public A {
int foo;
};
class C: public A {
C();
std::unique_ptr<A> _ptr; // changing to std::unique_ptr<B> _ptr removes the "class A has no member 'foo'" error
};
C::C() : A()
{
_ptr = std::make_unique<B>(); // no errors here
int w = _ptr->foo; // class A has no member 'foo'
}
When you assign
_ptr = std::make_unique<B>();
This works because B is a derived class of A, however _ptr is still a unique_ptr to the base class. You can't change the type of a variable after it's declared.
So what are your options?
Because you know that _ptr stores a pointer to the derived class B, you can do a cast after dereferencing it:
_ptr = std::make_unique<B>();
// derefence the pointer, and cast the reference to `B&`.
B& reference_to_sister = (B&)(*_ptr);
int w = reference_to_sister.foo;
If you take this approach, you'll have to somehow keep track of which derived class is in _ptr, or you'll run the risk of running into bugs.
Alternatively, if you're using C++17, you can use std::variant:
class C : public A {
void initialize(A& a) {
// Do stuff if it's the base class
}
void initialize(B& b) {
// Do different stuff if it's derived
int w = b.foo;
}
C() {
_ptr = std::make_unique<B>(); // This works
// This takes the pointer, and calls 'initialize'
auto initialize_func = [&](auto& ptr) { initialize(*ptr); };
// This will call 'initialize(A&)' if it contains A,
// and it'll call 'initialize(B&)' if it contains B
std::visit(initialize_func, _ptr);
}
std::variant<std::unique_ptr<A>, std::unique_ptr<B>> _ptr;
};
In fact, if you use std::variant this will work even if A and B are completely unrelated classes.
Here's another short variant example
#include <variant>
#include <string>
#include <iostream>
void print(std::string& s) {
std::cout << "String: " << s << '\n';
}
void print(int i) {
std::cout << "Int: " << i << '\n';
}
void print_either(std::variant<std::string, int>& v) {
// This calls `print(std::string&) if v contained a string
// And it calls `print(int)` if v contained an int
std::visit([](auto& val) { print(val); }, v);
}
int main() {
// v is empty right now
std::variant<std::string, int> v;
// Put a string in v:
v = std::string("Hello, world");
print_either(v); //Prints "String: Hello, world"
// Put an int in v:
v = 13;
print_either(v); //Prints "Int: 13"
}
simple multi-inheritance
struct A {};
struct B {};
struct C : A, B {};
or virtual inheritance
struct B {};
struct C : virtual B {};
Please note types are not polymorphic.
Custom memory allocation:
template <typedef T, typename... Args>
T* custom_new(Args&& args...)
{
void* ptr = custom_malloc(sizeof(T));
return new(ptr) T(std::forward<Args>(args)...);
}
template <typedef T>
void custom_delete(T* obj)
{
if (!obj)
return obj;
void* ptr = get_allocated_ptr(obj); // here
assert(std::is_polymorphic_v<T> || ptr == obj);
obj->~T();
custom_free(ptr); // heap corruption if assert ^^ failed
}
B* b = custom_new<C>(); // b != address of allocated memory
custom_delete(b); // UB
How can I implement get_allocated_ptr for non polymorphic types? For polymorphic types dynamic_cast<void*> does the job.
Alternatively I could check that obj is a pointer to a base class as deleting a non polymorphic object by a pointer to base class is UB. I don't know how to do this or if it's possible at all.
operator delete properly deallocates memory in such cases (e.g. VC++), though standard says it's UB. How does it do this? compiler-specific feature?
You actually have a more serious problem than getting the address of the full object. Consider this example:
struct Base
{
std::string a;
};
struct Derived : Base
{
std::string b;
};
Base* p = custom_new<Derived>();
custom_delete(p);
In this example, custom_delete will actually free the correct address (static_cast<void*>(static_cast<Derived*>(p)) == static_cast<void*>(p)), but the line obj->~T() will invoke the destructor for Base, meaning that the b field is leaked.
So Don't Do That
Instead of returning a raw pointer from custom_new, return an object that is bound to the type T and that knows how to delete it. For example:
template <class T> struct CustomDeleter
{
void operator()(T* object) const
{
object->~T();
custom_free(object);
}
};
template <typename T> using CustomPtr = std::unique_ptr<T, CustomDeleter<T>>;
template <typename T, typename... Args> CustomPtr<T> custom_new(Args&&... args)
{
void* ptr = custom_malloc(sizeof(T));
try
{
return CustomPtr<T>{ new(ptr) T(std::forward<Args>(args)...) };
}
catch (...)
{
custom_free(ptr);
throw;
}
}
Now it's impossible to accidentally free the wrong address and call the wrong destructor because the only code that calls custom_free knows the complete type of the thing that it's deleting.
Note: Beware of the unique_ptr::reset(pointer) method. This method is extremely dangerous when using a custom deleter since the onus is on the caller to supply a pointer that was allocated in the correct way. The compiler can't help if the method is called with an invalid pointer.
Passing Around Base Pointers
It may be that you want to both pass a base pointer to a function and give that function responsibility for freeing the object. In this case, you need to use type erasure to hide the type of the object from consumers while retaining knowledge of its most derived type internally. The easiest way to do that is with a std::shared_ptr. For example:
struct Base
{
int a;
};
struct Derived : Base
{
int b;
};
CustomPtr<Derived> unique_derived = custom_new<Derived>();
std::shared_ptr<Base> shared_base = std::shared_ptr<Derived>{ std::move(unique_derived) };
Now you can freely pass around shared_base and when the final reference is released, the complete Derived object will be destroyed and its correct address passed to custom_free. If you don't like the semantics of shared_ptr, it's fairly straightforward to create a type erasing pointer with unique_ptr semantics.
Note: One downside to this approach is that the shared_ptr requires a separate allocation for its control block (which won't use custom_malloc). With a little more work, you can get around that. You'd need to create a custom allocator that wraps custom_malloc and custom_free and then use std::allocate_shared to create your objects.
Complete Working Example
#include <memory>
#include <iostream>
void* custom_malloc(size_t size)
{
void* mem = ::operator new(size);
std::cout << "allocated object at " << mem << std::endl;
return mem;
}
void custom_free(void* mem)
{
std::cout << "freeing memory at " << mem << std::endl;
::operator delete(mem);
}
template <class T> struct CustomDeleter
{
void operator()(T* object) const
{
object->~T();
custom_free(object);
}
};
template <typename T> using CustomPtr = std::unique_ptr<T, CustomDeleter<T>>;
template <typename T, typename... Args> CustomPtr<T> custom_new(Args&&... args)
{
void* ptr = custom_malloc(sizeof(T));
try
{
return CustomPtr<T>{ new(ptr) T(std::forward<Args>(args)...) };
}
catch (...)
{
custom_free(ptr);
throw;
}
}
struct Base
{
int a;
~Base()
{
std::cout << "destroying Base" << std::endl;
}
};
struct Derived : Base
{
int b;
~Derived()
{
std::cout << "detroying Derived" << std::endl;
}
};
int main()
{
// Since custom_new has returned a unique_ptr with a deleter bound to the
// type Derived, we cannot accidentally free the wrong thing.
CustomPtr<Derived> unique_derived = custom_new<Derived>();
// If we want to get a pointer to the base class while retaining the ability
// to correctly delete the object, we can use type erasure. std::shared_ptr
// will do the trick, but it's easy enough to write a similar class without
// the sharing semantics.
std::shared_ptr<Base> shared_base = std::shared_ptr<Derived>{ std::move(unique_derived) };
// Notice that when we release the shared_base pointer, we destroy the complete
// object.
shared_base.reset();
}
You can do that only using dynamic_cast and static type of T has to be polymorphic. Otherwise look at this code:
struct A { int a; };
struct B { int b; };
struct C : A, B {};
B *b1 = new C, *b2 = new B;
If you try to delete by pointer to B, there is no way to know if b1 or b2 needs to be adjusted to get_allocated_ptr. One way or another you need B to be polymorphic to get pointer to most derived object.
What about a virtual interface that all structs inherit from, which returns the pointer at which the object was allocated? I had to make some changes to make the code compile. Both the multiple inheritance and virtual inheritance cases work:
#include <iostream>
#include <type_traits>
#include <cassert>
struct H {
public:
void* getHeader() { return header; }
void setHeader(void* ptr) { header = ptr; }
private:
void* header;
};
// multiple inheritance case
//struct A : public virtual H { int a;};
//struct B : public virtual H { int b;};
//struct C : A, B { };
// virtual inheritance case
struct B : public virtual H { int b; };
struct C : virtual B {};
template <typename T, typename ...Args>
T* custom_new(Args&&... args) {
void* ptr = malloc(sizeof(T));
T* obj = new(ptr) T(std::forward<Args>(args)...);
obj->setHeader(ptr);
return obj;
}
template <typename T>
void* get_allocated_ptr(T* obj) {
return obj->getHeader();
}
template <typename T>
void custom_delete(T* obj) {
void* ptr = get_allocated_ptr(obj); // here
// assert(std::is_polymorphic<T>::value || ptr == obj); // had to comment
obj->~T();
free(ptr); // heap corruption if assert ^^ failed
}
using namespace std;
int main(int argc, char *argv[]) {
C* c = custom_new<C>(); // b != address of allocated memory
std::cout << "PTR \t\t= " << c << std::endl;
auto b = static_cast<B*>(c);
std::cout << "CAST PTR \t= " << b << std::endl;
std::cout << "ALLOCATED PTR \t= " << get_allocated_ptr(b) << std::endl;
custom_delete(b); // UB
}
You can run this with either hierarchy, and the output is something like
PTR = 0x7f9fd4d00b90
CAST PTR = 0x7f9fd4d00b98
ALLOCATED PTR = 0x7f9fd4d00b90
although in the multiple inheritance case the pointers differ by 16 bits rather than 8 (because of the two integers).
This implementation could be improved by using templates to enable custom_new and the other functions only for structs inheriting from the H interface.
Consider the following class structure:-
class foo {
public:
int fun () {
cout << "in foo" << endl;
}
};
class bar_class1:public foo {
public:
int fun () {
cout << "in bar_class1" << endl;
}
};
class bar_class2:public foo {
public:
float fun () {
cout << "in bar_class2" << endl;
}
};
main () {
foo * foo_pointer = new bar_class1();
foo_pointer->fun();
}
The output of the above program is in foo. Is there a way, that using a pointer of type foo * which actually points to an object of type bar_class1 or bar_class2, we can call the fun function of the derived class instead of the base class? I am not able to make the fun function virtual in the base class foo since, then there is a return type conflict for function foo in the derived class bar_class2.
Here's my comments as an answer.
You cannot do that.
If that kind of polymorphism were possible, wouldn't that break horribly when code calls foo::fun (expecting an int) on an object whose actual type is bar_class2 and thus gets a float? Do you want to simply throw away type safety?
If you want different return types, sounds like you want a template. But you cannot use templates quite in the way that you want to use foo(). Static polymorphism (templates) and run time polymorphism (late binding) don't mix well. You need to redesign your oop structure.
If you absolutely hate type safety, you can sort of do this with void pointers. But for the love of Flying Spaghetti Monster, don't ever do this in c++. Please close your eyes before reading the following code to avoid exposure.
#include <iostream>
class foo {
public:
virtual void* fun() = 0;
virtual ~foo(){};
};
class bar_class1: public foo {
public:
void* fun() {
return &value;
}
private:
int value = 1;
};
class bar_class2: public foo {
public:
void* fun() {
return &value;
}
private:
float value = 1.1;
};
int main() {
foo* foo_pointer1 = new bar_class1();
foo* foo_pointer2 = new bar_class2();
// in c++ compiler must know the type of all objects during compilation
std::cout << *reinterpret_cast<int*>(foo_pointer1->fun()) << '\n';
std::cout << *reinterpret_cast<float*>(foo_pointer2->fun()) << '\n';
delete foo_pointer1;
delete foo_pointer2;
}
Perhaps similar to the existing answer, I really hope you realize changing your design is better than this mess, but I believe it's the best you're going to get. I force you to specify the return type at the callsite (e.g., someFoo->fun<int>()), since you're going to have to know it anyway, and dispatch based on that. Any funny business and you'll get an exception. Also keep in mind the performance of this is, I imagine, less than desirable.
#include <cassert>
#include <stdexcept>
#include <type_traits>
struct foo {
virtual ~foo() = default;
template<typename T, typename = typename std::enable_if<std::is_same<T, int>::value>::type, typename = void>
T fun();
template<typename T, typename = typename std::enable_if<std::is_same<T, float>::value>::type>
T fun();
};
struct bar_class1 : foo {
int fun() {
return 2;
}
};
struct bar_class2 : foo {
float fun() {
return 3.5f;
}
};
template<typename T, typename, typename Dummy>
T foo::fun() {
if (auto *p = dynamic_cast<bar_class1 *>(this)) {
return p->fun();
} else if (dynamic_cast<bar_class2 *>(this)) {
throw std::invalid_argument("Mismatching dynamic type.");
} else {
return 1;
}
}
template<typename T, typename>
T foo::fun() {
auto *p = dynamic_cast<bar_class2 *>(this);
if (dynamic_cast<bar_class1 *>(this) || !p) {
throw std::invalid_argument("Mismatching dynamic type.");
} else if (auto *p = dynamic_cast<bar_class2 *>(this)) {
return p->fun();
}
assert(false); //should never get here, but compiler doesn't know that
}
If you'd like the main function, I've written a complete sample.
To answer your question: No, late binding isn't possible without deciding the return type. ...at least not in a reasonable manner, see user2079303's great counter-example. But...
you may change your code (for example) into something like the following, using the keyword virtual and equalize the return type for instance to void:
class foo
{
public:
virtual void fun(std::ostream& out) {
out << "in foo" << std::endl;
}
};
so you can decide the output type later:
class intFoo: public foo
{
public:
void fun(std::ostream& out) {
// output an int
out << "in bar_class1. data: " << data << endl;
}
int data;
};
class floatFoo: public foo
{
public:
void fun(std::ostream& out) {
// output a float
out << "in bar_class2. data: " << data << endl;
}
float data;
};
For brevity, I double-use the output stream - now a parameter of the function fun() - function to demonstrate type-dependent portion of your derived class. In your application, the parameter will probably be of another, more useful type.
The function fun is not a virtual function since you didn't use the keyword "virtual" to decorate it. So, the compile will determine which function to call at compiling time. So, there is no way to tell the compiler to call another function because the compiler will use its static type, i.e. the variable definition type -- foo *.
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() );