Why can't I have std::optional<T> where T is abstract? - c++

This does not work:
struct Type {
virtual bool func(const std::string& val) const noexcept = 0;
}
// in main
optional<Type> = some_function_returning_optional_type();
and fails with a error message:
error: cannot declare field 'std::experimental::fundamentals_v1::_Optional_base<Type, false>::<anonymous union>::_M_payload' to be of abstract type 'Type'
Changing the Type to have a non-pure function works, but is not appropriate in this case, because there cannot be an instance of Type in my code, only classes which inherit from it should be able to exist.

std::optional<T> stores its value in-place - it therefore needs to know the size of T to work correctly, and T must be a concrete type that can be instantiated. You can think of std::optional<T> as:
template <typename T>
struct optional
{
std::aligned_storage_t<sizeof(T), alignof(T)> _data;
bool _set;
};
An abstract type represents an interface - polymorphism and some sort of indirection are required to work with abstract types. std::optional doesn't have any indirection by design.

Your proposal of optional will of course work but it would offend me to have to write
x.value()->do_something();
and I'd be concerned that users might do something daft:
x.value().reset(); // now what?
We can achieve polymorphism with a non-polymorphic interface by using a wrapper.
Here's one way:
#include <optional>
#include <iostream>
// the Foo interface/base class
struct Foo
{
virtual ~Foo() = default;
virtual Foo* clone() const { return new Foo(*this); }
virtual void do_something() {
std::cout << "something Fooey\n";
}
};
// a service for managing Foo and classes derived from Foo
struct FooService
{
template<class Arg>
Foo* clone(Arg&& arg)
{
using d_type = std::decay_t<Arg>;
return new d_type(std::forward<Arg>(arg));
}
template<class Arg>
Foo* clone(Foo* arg)
{
return arg->clone();
}
Foo* release(Foo*& other) noexcept
{
auto tmp = other;
other = nullptr;
return tmp;
}
};
// implement the Foo interface in terms of a pimpl
template<class Holder>
struct BasicFoo
{
decltype(auto) do_something() {
return get().do_something();
}
private:
Foo& get() noexcept { return static_cast<Holder*>(this)->get_impl(); }
Foo const& get() const noexcept { return static_cast<Holder const*>(this)->get_impl(); }
};
// a type for holding anything derived from a Foo
// can be initialised by anything Foo-like and handles copy/move correctly
struct FooHolder : BasicFoo<FooHolder>
{
template
<
class Arg,
std::enable_if_t
<
std::is_base_of_v<Foo, std::decay_t<Arg>>
>* = nullptr
>
FooHolder(Arg&& arg)
: service_()
, ptr_(service_.clone(std::forward<Arg>(arg)))
{}
FooHolder(FooHolder const& other)
: service_()
, ptr_(other.ptr_->clone())
{
}
FooHolder(FooHolder && other) noexcept
: service_()
, ptr_(service_.release(other.ptr_))
{
}
FooHolder& operator=(FooHolder const& other)
{
auto tmp = other;
std::swap(ptr_, tmp.ptr_);
return *this;
}
FooHolder& operator=(FooHolder && other) noexcept
{
auto tmp = std::move(other);
std::swap(ptr_, tmp.ptr_);
return *this;
}
~FooHolder()
{
delete ptr_;
}
Foo& get_impl() noexcept { return *ptr_; }
Foo const& get_impl() const noexcept { return *ptr_; }
FooService service_;
Foo* ptr_;
};
// now we can supply as many overrides of Foo as we like
struct Bar : Foo
{
virtual Foo* clone() const { return FooService().clone(*this); }
virtual void do_something() {
std::cout << "something Barey\n";
}
};
int main()
{
std::optional<FooHolder> opt;
// note that we're initialising cleanly
opt = Bar {};
// and we don't expose the pointer so the user can't
// destroy the pimpl accidentally
opt.value().do_something();
}

Related

How can I convert type-erasure wrappers or use them as another wrapper

I created sets of functions using the type-erasure design pattern:
Encodable: encode(), decode()
Printable: print()
If I overload these functions with MyStruct1 and MyStruct2, I'll be able to wrap these types in the type-erasure wrappers Encodable and Printable and indirectly call those functions using the wrappers functions and store MyStruct1 and MyStruct2 in a heterogeneous container like std::vector<Encodable>.
My problem arised when I wanted to use these objects both as an Encodable and a Printable or when I wanted to convert one to the other.
#include <cstdint>
#include <utility>
#include <memory>
#include <vector>
class Encodable {
private:
struct EncodableConcept {
virtual ~EncodableConcept() = default;
virtual std::vector<uint8_t> _encode() const = 0;
virtual bool _decode(std::vector<uint8_t> const& byteVector) = 0;
virtual std::unique_ptr<EncodableConcept> clone() const = 0;
};
template<typename EncodableT>
struct EncodableModel : public EncodableConcept {
EncodableModel(EncodableT inst) : _inst{std::move(inst)}
{}
std::vector<uint8_t> _encode() const override {
return encode(_inst);
}
bool _decode(std::vector<uint8_t> const& byteVector) override {
return decode(_inst, byteVector);
}
std::unique_ptr<EncodableConcept> clone() const override {
return std::make_unique<EncodableModel>(*this);
}
EncodableT _inst;
};
friend std::vector<uint8_t> encode(Encodable const& inst) {
return inst.pimpl->_encode();
}
friend bool decode(Encodable & inst, std::vector<uint8_t> const& byteVector) {
return inst.pimpl->_decode(byteVector);
}
public:
template<typename EncodableT>
Encodable(EncodableT inst)
: pimpl{std::make_unique<EncodableModel<EncodableT>>(std::move(inst))}
{}
Encodable(Encodable const& other)
: pimpl(other.pimpl->clone())
{}
Encodable& operator=(Encodable const& other) {
// Copy-and-swap idiom
Encodable tmp(other);
std::swap(pimpl, tmp.pimpl);
return *this;
}
// Move is not implemented to prevent the pimpl to be nullptr after move operation.
// Upon move the copy constructor or the copy assignment operator is going to be called.
private:
std::unique_ptr<EncodableConcept> pimpl;
};
class Printable {
private:
struct PrintableConcept {
virtual ~PrintableConcept() = default;
virtual void _print() const = 0;
virtual std::unique_ptr<PrintableConcept> clone() const = 0;
};
template<typename PrintableT>
struct PrintableModel : public PrintableConcept {
PrintableModel(PrintableT inst) : _inst{std::move(inst)}
{}
void _print() const override {
print(_inst);
}
std::unique_ptr<PrintableConcept> clone() const override {
return std::make_unique<PrintableModel>(*this);
}
PrintableT _inst;
};
friend void print(Printable const& inst) {
inst.pimpl->_print();
}
public:
template<typename PrintableT>
Printable(PrintableT inst)
: pimpl{std::make_unique<PrintableModel<PrintableT>>(std::move(inst))}
{}
Printable(Printable const& other)
: pimpl(other.pimpl->clone())
{}
Printable& operator=(Printable const& other) {
// Copy-and-swap idiom
Printable tmp(other);
std::swap(pimpl, tmp.pimpl);
return *this;
}
// Move is not implemented to prevent the pimpl to be nullptr after move operation.
// Upon move the copy constructor or the copy assignment operator is going to be called.
private:
std::unique_ptr<PrintableConcept> pimpl;
};
struct MyStruct1 {
MyStruct1(int x) : x{x} {}
int x;
};
std::vector<uint8_t> encode(MyStruct1 const& inst) {
std::vector<uint8_t> byteVector;
// ...
return byteVector;
}
bool decode(MyStruct1 & inst, std::vector<uint8_t> const& byteVector) {
// ...
return true; // Success
}
void print(MyStruct1 const& inst) {
printf("MyStruct1{%d}\n", inst.x);
}
struct MyStruct2 {
MyStruct2(float y) : y{y} {}
float y;
};
std::vector<uint8_t> encode(MyStruct2 const& inst) {
std::vector<uint8_t> byteVector;
// ...
return byteVector;
}
bool decode(MyStruct2 & inst, std::vector<uint8_t> const& byteVector) {
// ...
return true; // Success
}
void print(MyStruct2 const& inst) {
printf("MyStruct2{%f}\n", inst.y);
}
std::vector<Encodable> readFromSomewhere() {
std::vector<Encodable> readEncodables;
// Read from file, socket, etc...
readEncodables = {MyStruct1{1}, MyStruct2{2.2}, MyStruct1{3}, MyStruct2{4.4}};
return readEncodables;
}
int main(int argc, char** argv) {
std::vector<Encodable> readEncodables = readFromSomewhere();
// TODO: How can I print readEncodables using the overloaded print functions?
// For example:
for (Encodable const& obj : readEncodables) {
// print(obj); // Compilation error (thanks god)
}
// TODO: How can I convert an Encodable to a Printable object?
std::vector<Printable> convertedPrintables;
for (Encodable const& obj : readEncodables) {
// convertedPrintables.emplace_back(obj); // Compilation error (thanks god)
// print(*convertedPrintables.crbegin());
}
return 0;
}
How can I easily convert an Encodable to a Printable and vice versa?
Can I somehow directly use an Encodable as a Printable if the print() function is overloaded for the given type?
My only (bad) solution is to create a templated get() function in the wrappers to unwrap the contained object run-time. If the type of the template argument matches the object the unwrap succeeds and I can rewrap it into a Printable or just directly use the overloaded print() free function.
template<typename EncodableT>
EncodableT* get() const {
EncodableModel<EncodableT>* modelPtr = dynamic_cast<EncodableModel<EncodableT>*> pimpl.get());
if (modelPtr == nullptr) return nullptr;
return &modelPtr->_inst;
}
However the usage of this function would be rather ugly and inconvenient:
for (Encodable const& obj : readEncodables) {
auto* ptr1 = obj.get<MyStruct1>();
if (ptr1 != nullptr) {
print(*ptr1);
continue;
}
auto* ptr2 = obj.get<MyStruct2>();
if (ptr2 != nullptr) {
print(*ptr2);
continue;
}
// ...
}
This could be made a bit nicer by storing std::type_index the wrappers, but I'd still have create if-else or switch statements for each type for calling a simple print() free function...

What is the reason of QVector's requirement for default constructor?

I can see that that classes are treated as complex objects which are required for calling default constructor:
void QVector<T>::defaultConstruct(T *from, T *to)
{
if (QTypeInfo<T>::isComplex) {
while (from != to) {
new (from++) T();
}
...
}
But it's not clear why is it needed to construct objects in the 'hidden' area of QVector. I mean these objects are not accessible at all, so why not just to reserve the memory instead of the real object creation?
And as a bonus question, I would like to ask, if I want to have an array of non-default-constractible objects, can I safely replace QVector<T> with QVector<Wrapper<T>? where Wrapper is something like that:
class Wrapper {
public:
union {
T object;
bool hack;
};
Wrapper() {}
Wrapper(const T &t) : object { t } {}
Wrapper(const Wrapper &t) : object { t.object } {}
Wrapper &operator=(const Wrapper &value) {
object = value.object;
return *this;
}
~Wrapper() {}
};
It's easy enough to make the QVector work for a non-default-constructible type T:
#define QVECTOR_NON_DEFAULT_CONSTRUCTIBLE(Type) \
template <> QVector<Type>::QVector(int) = delete; \
template <> void QVector<Type>::resize(int newSize) { \
Q_ASSERT(newSize <= size()); \
detach(); \
} \
template <> void QVector<Type>::defaultConstruct(Type*, Type*) { Q_ASSERT(false); }
The macro needs to be present right after MyType declaration - in the header file (if any), and it must be in namespace or global scope:
struct MyType { ... };
QVECTOR_NON_DEFAULT_CONSTRUCTIBLE(MyType)
struct A {
struct MyType2 { ... };
};
QVECTOR_NON_DEFAULT_CONSTRUCTIBLE(A::MyType2);
No, the wrapper is not correct. It doesn't destruct the object member. It also doesn't offer move semantics, doesn't protect from being default-constructed, etc. The hack union member is not necessary. Nothing in a union will be default-constructed for you.
Here's a more correct wrapper - it pretty much resembles std::optional. See here to see how much nuance an optional needs :)
// https://github.com/KubaO/stackoverflown/tree/master/questions/vector-nodefault-33380402
template <typename T> class Wrapper final {
union {
T object;
};
bool no_object = false;
void cond_destruct() {
if (!no_object)
object.~T();
no_object = true;
}
public:
Wrapper() : no_object(true) {}
Wrapper(const Wrapper &o) : no_object(o.no_object) {
if (!no_object)
new (&object) T(o.object);
}
Wrapper(Wrapper &&o) : no_object(o.no_object) {
if (!no_object)
new (&object) T(std::move(o.object));
}
Wrapper(const T &o) : object(o) {}
Wrapper(T &&o) : object(std::move(o)) {}
template <class...Args> Wrapper(Args...args) : object(std::forward<Args>(args)...) {}
template <class U, class...Args> Wrapper(std::initializer_list<U> init, Args...args) :
object(init, std::forward<Args>(args)...) {}
operator T& () & { assert(!no_object); return object; }
operator T&& () && { assert(!no_object); return std::move(object); }
operator T const&() const& { assert(!no_object); return object; }
Wrapper &operator=(const Wrapper &o) & {
if (no_object)
::new (&object) T(o);
else
object = o.object;
no_object = false;
return *this;
}
Wrapper &operator=(Wrapper &&o) & {
if (no_object)
::new (&object) T(std::move(o.object));
else
object = std::move(o.object);
no_object = false;
return *this;
}
template<class... Args> T &emplace(Args&&... args) {
cond_destruct();
::new (&object) T(std::forward<Args>(args)...);
no_object = false;
return object;
}
~Wrapper() {
cond_destruct();
}
};
Since the assignment operators are ref-qualified, it disallows assigning to rvalues, so it has the IMHO positive property that the following won't compile:
Wrapper<int>() = 1 // likely Wrapper<int>() == 1 was intended

What's the best way to implement AST using visitor pattern with return value?

I'm trying to implement a simple abstract syntax tree (AST) in C++ using the visitor pattern. Usually a visitor pattern does not handle return value. But in my AST there are expressions nodes which care about the return type and value of its children node. For example, I have a Node structure like this:
class AstNode
{
public:
virtual void accept(AstNodeVisitor&) = 0;
void addChild(AstNode* child);
AstNode* left() { return m_left; }
AstNode* right() { return m_right; }
...
private:
AstNode* m_left;
AstNode* m_right;
};
class CompareNode : public AstNode
{
public:
virtual void accept(AstNodeVisitor& v)
{
v->visitCompareNode(this);
}
bool eval(bool lhs, bool rhs) const
{
return lhs && rhs;
}
};
class SumNode : public AstNode
{
public:
virtual void accept(AstNodeVisitor& v)
{
v->visitSumNode(this);
}
int eval(int lhs, int rhs) const
{
return lhs + rhs;
}
};
class AstNodeVisitor
{
public:
...
bool visitCompareNode(CompareNode& node)
{
// won't work, because accept return void!
bool lhs = node.left()->accept(*this);
bool rhs = node.right()->accept(*this);
return node.eval(lhs, rhs);
}
int visitSumNode(Node& node)
{
// won't work, because accept return void!
int lhs = node.left()->accept(*this);
int rhs = node.right()->accept(*this);
return node.eval(lhs, rhs);
}
};
In this case both CompareNode and SumNode are binary operators but they rely on the return type of their children's visit.
As far as I can see to make it work, there are only 2 options:
accept can still return void, save the return value in a context object which is passed to each accept and visit function, and use them in the visit function, where I know what type to use. This should work but feels like a hack.
make AstNode a template, and accept function a none virtual, but return type depends on template parameter T.But if I do this, I no longer have a common AstNode* class and can't save any AstNode* in the children list.
for example:
template <typename T`>
class AstNode
{
public:
T accept(AstNodeVisitor&);
...
};
So is there a more elegant way to do this? This should be a fairly common problem for people implementing AST walking so I'd like to know what's the best practice.
Thanks.
The Visitor can have member that it can use to store result, something like:
class AstNodeVisitor
{
public:
void visitCompareNode(CompareNode& node)
{
node.left()->accept(*this); // modify b
bool lhs = b;
node.right()->accept(*this); // modify b
bool rhs = b;
b = node.eval(lhs, rhs);
}
void visitSumNode(Node& node)
{
node.left()->accept(*this); // modify n
int lhs = n;
node.right()->accept(*this); // modify n
int rhs = n;
n = node.eval(lhs, rhs);
}
private:
bool b;
int n;
};
You may also want to save the type of last result or use something like boost::variant.
template<class T> struct tag { using type=T; };
template<class...Ts> struct types { using type=types; }
template<class T>
struct AstVisitable {
virtual boost::optional<T> accept( tag<T>, AstNodeVisitor&v ) = 0;
virtual ~AstVisitable() {};
};
template<>
struct AstVisitable<void> {
virtual void accept( tag<void>, AstNodeVisitor&v ) = 0;
virtual ~AstVisitable() {};
};
template<class Types>
struct AstVisitables;
template<>
struct AstVisibables<types<>> {
virtual ~AstVisitables() {};
};
template<class T0, class...Ts>
struct AstVisitables<types<T0, Ts...>>:
virtual AstVisitable<T0>,
AstVisitables<types<Ts...>>
{
using AstVisitable<T0>::accept;
using AstVisitables<types<Ts...>>::accept;
};
using supported_ast_return_types = types<int, bool, std::string, void>;
class AstNode:public AstVisitables<supported_ast_return_types> {
public:
void addChild(AstNode* child);
AstNode* left() { return m_left.get(); }
AstNode* right() { return m_right.get(); }
private:
std::unique_ptr<AstNode> m_left;
std::unique_ptr<AstNode> m_right;
};
template<class types>
struct AstVisiablesFailAll;
template<>
struct AstVisiablesFailAll<> {
virtual ~AstVisiablesFailAll() {};
};
template<class T>
struct AstVisitableFailure : virtual AstVisitable<T> {
boost::optional<T> accept( tag<T>, AstNodeVisitor& ) override {
return {};
}
};
template<>
struct AstVisitableFailure<void> : virtual AstVisitable<void> {
void accept( tag<void>, AstNodeVisitor& ) override {
return;
}
};
template<class T0, class...Ts>
struct AstVisitablesFailAll<types<T0, Ts...>>:
AstVisitableFailure<T0>,
AstVisitableFailAll<types<Ts...>>
{
using AstVisitableFailure<T0>::accept;
using AstVisitableFailAll<types<Ts...>>::accept;
};
So now you can boost::optional<bool> lhs = node.left()->accept( tag<bool>, *this );, and from the state of lhs know if the left node can be evaluated in a bool context.
SumNode looks like this:
class SumNode :
public AstNode,
AstVisiablesFailAll<supported_ast_return_types>
{
public:
void accept(tag<void>, AstNodeVisitor& v) override
{
accept(tag<int>, v );
}
boost::optional<int> accept(tag<int>, AstNodeVisitor& v) override
{
return v->visitSumNode(this);
}
int eval(int lhs, int rhs) const {
return lhs + rhs;
}
};
and visitSumNode:
boost::optional<int> visitSumNode(Node& node) {
// won't work, because accept return void!
boost::optional<int> lhs = node.left()->accept(tag<int>, *this);
boost::optional<int> rhs = node.right()->accept(tag<int>, *this);
if (!lhs || !rhs) return {};
return node.eval(*lhs, *rhs);
}
The above assumes that visiting a+b in a void context is acceptable (like in C/C++). If it isn't, then you need a means for void visit to "fail to produce a void".
In short, accepting requires context, which also determines what type you expect. Failure is an empty optional.
The above uses boost::optional -- std::experimental::optional would also work, or you can roll your own, or you can define a poor man's optional:
template<class T>
struct poor_optional {
bool empty = true;
T t;
explicit operator bool() const { return !empty; }
bool operator!() const { return !*this; }
T& operator*() { return t; }
T const& operator*() const { return t; }
// 9 default special member functions:
poor_optional() = default;
poor_optional(poor_optional const&)=default;
poor_optional(poor_optional const&&)=default;
poor_optional(poor_optional &&)=default;
poor_optional(poor_optional &)=default;
poor_optional& operator=(poor_optional const&)=default;
poor_optional& operator=(poor_optional const&&)=default;
poor_optional& operator=(poor_optional &&)=default;
poor_optional& operator=(poor_optional &)=default;
template<class...Ts>
void emplace(Ts&&...ts) {
t = {std::forward<Ts>(ts)...};
empty = false;
}
template<class...Ts>
poor_optional( Ts&&... ts ):empty(false), t(std::forward<Ts>(ts)...) {}
};
which sucks, because it constructs a T even if not needed, but it should sort of work.
For completion sake I post the template version that is mentioned by the OP
#include <string>
#include <iostream>
namespace bodhi
{
template<typename T> class Beignet;
template<typename T> class Cruller;
template<typename T> class IPastryVisitor
{
public:
virtual T visitBeignet(Beignet<T>& beignet) = 0;
virtual T visitCruller(Cruller<T>& cruller) = 0;
};
template<typename T> class Pastry
{
public:
virtual T accept(IPastryVisitor<T>& visitor) = 0;
};
template<typename T> class Beignet : public Pastry<T>
{
public:
T accept(IPastryVisitor<T>& visitor)
{
return visitor.visitBeignet(*this);
}
std::string name = "Beignet";
};
template<typename T> class Cruller : public Pastry<T>
{
public:
T accept(IPastryVisitor<T>& visitor)
{
return visitor.visitCruller(*this);
}
std::string name = "Cruller";
};
class Confectioner : public IPastryVisitor<std::string>
{
public:
virtual std::string visitBeignet(Beignet<std::string>& beignet) override
{
return "I just visited: " + beignet.name;
}
virtual std::string visitCruller(Cruller<std::string>& cruller) override
{
return "I just visited: " + cruller.name;
}
};
}
int main()
{
bodhi::Confectioner pastryChef;
bodhi::Beignet<std::string> beignet;
std::cout << beignet.accept(pastryChef) << "\n";
bodhi::Cruller<std::string> cruller;
std::cout << cruller.accept(pastryChef) << "\n";
return 0;
}
Every pastry is a node and every visitor can implement its accepted return type. Having multiple visitor could visit the same pastry.

How to implement deep copy feature in some smart pointer?

unique_ptr is quite useful. However, it is not copyable. If virutal clone (deep copy) methods are provided for its pointed class, I think it will become more useful. Is it necessary or any better way to implement it? Any similar smart pointer exist in some library? Here is a version
template<class T>
class deep_ptr: private unique_ptr<T>
{
public:
using unique_ptr<T>::operator *;
using unique_ptr<T>::operator ->;
using unique_ptr<T>::operator bool;
using unique_ptr<T>::release;
using unique_ptr<T>::reset;
using unique_ptr<T>::get;
// add (DEFAULT_CONSTRUCTOR)(MOVE_CONSTRUCTOR)(MOVE_ASSIGNMENT_METHOD) ...
explicit deep_ptr(T* p) : unique_ptr(p) {}
deep_ptr(deep_ptr const& r) : unique_ptr(r->clone()) {}
deep_ptr& operator=(deep_ptrconst& r)
{ if (this != &r) reset(r->clone()); return *this; }
};
Juse feel it is very useful but never see similar things. ???
Unless I am misunderstanding what you are looking for, if a class has a clone method, that should be sufficient to get what you are looking for.
Sample code:
#include <iostream>
#include <memory>
struct A
{
virtual ~A() {}
virtual A* clone() = 0;
};
struct B : A
{
B(int in = 0) : x(in) {}
B(B const& copy) : x(copy.x) {}
virtual ~B() {std::cout << "In B::~B()\n";}
virtual A* clone() { return new B(*this); }
int x;
};
int main()
{
std::unique_ptr<A> p1(new B(10));
std::unique_ptr<A> p2(p1->clone());
return 0;
}
Output from running the above program:
In B::~B()
In B::~B()
Without a clone method (just a copy-constructor) the following should work:
template <typename T>
class deep_ptr
{
public:
deep_ptr() : i_() {}
deep_ptr(std::nullptr_t) : i_(nullptr) {}
template <typename U>
deep_ptr(U* u) : i_(u ? new inner_impl<U>(*u) : nullptr) {}
~deep_ptr() { delete i_; }
deep_ptr(const deep_ptr& p) : i_(p.i_ ? p.i_->copy() : nullptr) {}
deep_ptr& operator=(const deep_ptr& p)
{
if (!p.i_) { i_ = nullptr; }
else { i_ = p.i_->copy(); }
}
deep_ptr(deep_ptr&& p) : i_(p.i_) { p.i_ = nullptr; }
deep_ptr& operator=(deep_ptr&& p)
{
i_ = p.i_;
p.i_ = nullptr;
}
const T* operator->() const { return get(); }
const T* get() const
{
if (i_) { return *i_; }
return nullptr;
}
const T& operator*() const { return *static_cast<T*>(*i_); }
T* operator->() { return get(); }
T* get()
{
if (i_) { return *i_; }
return nullptr;
}
T& operator*(){ return *static_cast<T*>(*i_); }
private:
struct inner
{
virtual inner* copy() const = 0;
virtual operator const T*() const = 0;
virtual operator T*() = 0;
virtual ~inner() {}
};
inner* i_;
template <typename U>
struct inner_impl : inner
{
inner_impl(const U& u) : u_(u) {}
inner_impl* copy() const override { return new inner_impl(u_); }
operator const T*() const override { return &u_; }
operator T*() override { return &u_; }
U u_;
};
};

Dynamic Object in C++?

I realize that I'll most likely get a lot of "you shouldn't do that because..." answers and they are most welcome and I'll probably totally agree with your reasoning, but I'm curious as to whether this is possible (as I envision it).
Is it possible to define a type of dynamic/generic object in C++ where I can dynamically create properties that are stored and retrieved in a key/value type of system? Example:
MyType myObject;
std::string myStr("string1");
myObject.somethingIJustMadeUp = myStr;
Note that obviously, somethingIJustMadeUp is not actually a defined member of MyType but it would be defined dynamically. Then later I could do something like:
if(myObject.somethingIJustMadeUp != NULL);
or
if(myObject["somethingIJustMadeUp"]);
Believe me, I realize just how terrible this is, but I'm still curious as to whether it's possible and if it can be done in a way that minimizes it's terrible-ness.
C++Script is what you want!
Example:
#include <cppscript>
var script_main(var args)
{
var x = object();
x["abc"] = 10;
writeln(x["abc"]);
return 0;
}
and it's a valid C++.
You can do something very similar with std::map:
std::map<std::string, std::string> myObject;
myObject["somethingIJustMadeUp"] = myStr;
Now if you want generic value types, then you can use boost::any as:
std::map<std::string, boost::any> myObject;
myObject["somethingIJustMadeUp"] = myStr;
And you can also check if a value exists or not:
if(myObject.find ("somethingIJustMadeUp") != myObject.end())
std::cout << "Exists" << std::endl;
If you use boost::any, then you can know the actual type of value it holds, by calling .type() as:
if (myObject.find("Xyz") != myObject.end())
{
if(myObject["Xyz"].type() == typeid(std::string))
{
std::string value = boost::any_cast<std::string>(myObject["Xyz"]);
std::cout <<"Stored value is string = " << value << std::endl;
}
}
This also shows how you can use boost::any_cast to get the value stored in object of boost::any type.
This can be a solution, using RTTI polymorphism
#include <map>
#include <memory>
#include <iostream>
#include <stdexcept>
namespace dynamic
{
template<class T, class E>
T& enforce(T& z, const E& e)
{ if(!z) throw e; return z; }
template<class T, class E>
const T& enforce(const T& z, const E& e)
{ if(!z) throw e; return z; }
template<class Derived>
class interface;
class aggregate;
//polymorphic uncopyable unmovable
class property
{
public:
property() :pagg() {}
property(const property&) =delete;
property& operator=(const property&) =delete;
virtual ~property() {} //just make it polymorphic
template<class Interface>
operator Interface*() const
{
if(!pagg) return 0;
return *pagg; //let the aggregate do the magic!
}
aggregate* get_aggregate() const { return pagg; }
private:
template<class Derived>
friend class interface;
friend class aggregate;
static unsigned gen_id()
{
static unsigned x=0;
return enforce(++x,std::overflow_error("too many ids"));
}
template<class T>
static unsigned id_of()
{ static unsigned z = gen_id(); return z; }
aggregate* pagg;
};
template<class Derived>
class interface: public property
{
public:
interface() {}
virtual ~interface() {}
unsigned id() const { return property::id_of<Derived>(); }
};
//sealed movable
class aggregate
{
public:
aggregate() {}
aggregate(const aggregate&) = delete;
aggregate& operator=(const aggregate&) = delete;
aggregate(aggregate&& s) :m(std::move(s.m)) {}
aggregate& operator=(aggregate&& s)
{ if(this!=&s) { m.clear(); std::swap(m, s.m); } return *this; }
template<class Interface>
aggregate& add_interface(interface<Interface>* pi)
{
m[pi->id()] = std::unique_ptr<property>(pi);
static_cast<property*>(pi)->pagg = this;
return *this;
}
template<class Inteface>
aggregate& remove_interface()
{ m.erase[property::id_of<Inteface>()]; return *this; }
void clear() { m.clear(); }
bool empty() const { return m.empty(); }
explicit operator bool() const { return empty(); }
template<class Interface>
operator Interface*() const
{
auto i = m.find(property::id_of<Interface>());
if(i==m.end()) return nullptr;
return dynamic_cast<Interface*>(i->second.get());
}
template<class Interface>
friend aggregate& operator<<(aggregate& s, interface<Interface>* pi)
{ return s.add_interface(pi); }
private:
typedef std::map<unsigned, std::unique_ptr<property> > map_t;
map_t m;
};
}
/// this is a sample on how it can workout
class interface_A: public dynamic::interface<interface_A>
{
public:
virtual void methodA1() =0;
virtual void methodA2() =0;
};
class impl_A1: public interface_A
{
public:
impl_A1() { std::cout<<"creating impl_A1["<<this<<"]"<<std::endl; }
virtual ~impl_A1() { std::cout<<"deleting impl_A1["<<this<<"]"<<std::endl; }
virtual void methodA1() { std::cout<<"interface_A["<<this<<"]::methodA1 on impl_A1 in aggregate "<<get_aggregate()<<std::endl; }
virtual void methodA2() { std::cout<<"interface_A["<<this<<"]::methodA2 on impl_A1 in aggregate "<<get_aggregate()<<std::endl; }
};
class impl_A2: public interface_A
{
public:
impl_A2() { std::cout<<"creating impl_A2["<<this<<"]"<<std::endl; }
virtual ~impl_A2() { std::cout<<"deleting impl_A2["<<this<<"]"<<std::endl; }
virtual void methodA1() { std::cout<<"interface_A["<<this<<"]::methodA1 on impl_A2 in aggregate "<<get_aggregate()<<std::endl; }
virtual void methodA2() { std::cout<<"interface_A["<<this<<"]::methodA2 on impl_A2 in aggregate "<<get_aggregate()<<std::endl; }
};
class interface_B: public dynamic::interface<interface_B>
{
public:
virtual void methodB1() =0;
virtual void methodB2() =0;
};
class impl_B1: public interface_B
{
public:
impl_B1() { std::cout<<"creating impl_B1["<<this<<"]"<<std::endl; }
virtual ~impl_B1() { std::cout<<"deleting impl_B1["<<this<<"]"<<std::endl; }
virtual void methodB1() { std::cout<<"interface_B["<<this<<"]::methodB1 on impl_B1 in aggregate "<<get_aggregate()<<std::endl; }
virtual void methodB2() { std::cout<<"interface_B["<<this<<"]::methodB2 on impl_B1 in aggregate "<<get_aggregate()<<std::endl; }
};
class impl_B2: public interface_B
{
public:
impl_B2() { std::cout<<"creating impl_B2["<<this<<"]"<<std::endl; }
virtual ~impl_B2() { std::cout<<"deleting impl_B2["<<this<<"]"<<std::endl; }
virtual void methodB1() { std::cout<<"interface_B["<<this<<"]::methodB1 on impl_B2 in aggregate "<<get_aggregate()<<std::endl; }
virtual void methodB2() { std::cout<<"interface_B["<<this<<"]::methodB2 on impl_B2 in aggregate "<<get_aggregate()<<std::endl; }
};
int main()
{
dynamic::aggregate agg1;
agg1 << new impl_A1 << new impl_B1;
dynamic::aggregate agg2;
agg2 << new impl_A2 << new impl_B2;
interface_A* pa = 0;
interface_B* pb = 0;
pa = agg1; if(pa) { pa->methodA1(); pa->methodA2(); }
pb = *pa; if(pb) { pb->methodB1(); pb->methodB2(); }
pa = agg2; if(pa) { pa->methodA1(); pa->methodA2(); }
pb = *pa; if(pb) { pb->methodB1(); pb->methodB2(); }
agg2 = std::move(agg1);
pa = agg2; if(pa) { pa->methodA1(); pa->methodA2(); }
pb = *pa; if(pb) { pb->methodB1(); pb->methodB2(); }
return 0;
}
tested with MINGW4.6 on WinXPsp3
Yes it is terrible. :D
It had been done numerous times to different extents and success levels.
QT has Qobject from which everything related to them decends.
MFC has CObject from which eveything decends as does C++.net
I don't know if there is a way to make it less bad, I guess if you avoid multiple inheritance like the plague (which is otherwise a useful language feature) and reimplement the stdlib it would be better. But really if that is what you are after you are probably using the wrong language for the task.
Java and C# are much better suited to this style of programming.
#note if I have read your question wrong just delete this answer.
Check out Dynamic C++