Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implementing a "clone" function with rttr #217

Open
abhean opened this issue Jan 19, 2019 · 2 comments
Open

Implementing a "clone" function with rttr #217

abhean opened this issue Jan 19, 2019 · 2 comments

Comments

@abhean
Copy link

abhean commented Jan 19, 2019

Hi,

I am trying to use rttr to implement a "clone" function. It would be something like this:

class Shape {};
class Circle : public Shape {};

std::unique_ptr<Shape> cloneShape(const Shape* shape)
{
  rttr::variant v_shapePtr(shape);
  ???
}

From what I know, rttr already has all the pieces it would need to do that: a way of getting the shape memory address, a function to get the shape "most derived type" (E,g,: Circle); access to the classes copy constructors and a way of wrapping the result in a std::unique_ptr; but I can't find a way of putting all these pieces together (or even accessing them since most of them are rttr::type or rttr::variant private members). I would really appreciate if you could point me in the right direction.

Thanks a lot.

@acki-m
Copy link
Contributor

acki-m commented Jan 22, 2019

You can register this function directly, but without returning a std::unique_ptr<Shape>, instead you should return a std::shared_ptr<Shape>. Because the returned type, needs to be copy able.
Then you have you clone function implemented.

Does this help you?

@abhean
Copy link
Author

abhean commented Jan 26, 2019

Hi,

thanks for your answer. In fact, what I am trying to do is implementing the "clone" function body using rttr. My current "clone" implementation would be something like this:

class Base
{
public:

  virtual ~Base() = default;

  virtual std::unique_ptr<Base> Clone() const
  {
    return std::unique_ptr<Base>(new Base(*this));
  }
};


class Derived : public Base
{
public:

  std::unique_ptr<Base> Clone() const override
  {
    return std::unique_ptr<Base>(new Derived(*this));
  }
};

int main()
{
  std::unique_ptr<Base> instance(new Derived());
  std::unique_ptr<Base> copy(instance->Clone()); // Derived instance copy
}

I want to take advantage of rttr so that the derived classes no longer need to implement a virtual Clone function. I have come up with an implementation that could work with a minor change to rttr:

class Base
{
public:

  virtual ~Base() = default;

  std::unique_ptr<Base> Clone() const
  {
    rttr::type mostDerivedType = rttr::type::get(*this);
    // ERROR: It tries to call the most derived type (Derived) copy constructor, 
    // but rttr::argument gets the wrong type (Base instead of Derived)
    rttr::variant copyRawPtr = mostDerivedType.create({ rttr::argument(*this) }); 
    assert(copyRawPtr.is_valid() && copyRawPtr.get_type().is_pointer());
    copyRawPtr.convert(rttr::type::get<Base*>());
    return std::unique_ptr<Base>(copyRawPtr.get_value<Base*>());
  }

  RTTR_ENABLE()
};


class Derived : public Base
{
  RTTR_ENABLE(Base)
};

RTTR_REGISTRATION
{
  rttr::registration::class_<Base>("Base").constructor<const Base&>()(rttr::policy::ctor::as_raw_ptr);
  rttr::registration::class_<Derived>("Derived").constructor<const Derived&>()(rttr::policy::ctor::as_raw_ptr);
}

int main()
{
  std::unique_ptr<Base> instance(new Derived());
  std::unique_ptr<Base> copy(instance->Clone()); // Derived instance copy
}

The only problem with this implementation right now is that rttr::argument type is initialized from its argument static type. In the previous example, rttr::argument type would be Base even if its argument is a Derived class instance. This could be fixed if the rttr::argument constructor implementation captures the dynamic type of the passed data instead of its static type.

Now we have:

template<typename T, typename Tp>
argument::argument(const T& data) RTTR_NOEXCEPT
:   m_data(reinterpret_cast<const void*>(std::addressof(data))),
    m_variant(nullptr),
    m_type(rttr::type::get<T>())
{
    static_assert(!std::is_same<instance, T>::value, "Don't use the argument class for forwarding an instance!");
}

And it would change to:

template<typename T, typename Tp>
argument::argument(const T& data) RTTR_NOEXCEPT
:   m_data(reinterpret_cast<const void*>(std::addressof(data))),
    m_variant(nullptr),
    m_type(rttr::type::get(data)) // gets data dynamic type
{
    static_assert(!std::is_same<instance, T>::value, "Don't use the argument class for forwarding an instance!");
}

This seems to work for me (and it passes all the library tests). Do you think it makes sense?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants