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

boost::optional<T> as properties #29

Closed
MicBe opened this issue Dec 14, 2016 · 6 comments
Closed

boost::optional<T> as properties #29

MicBe opened this issue Dec 14, 2016 · 6 comments

Comments

@MicBe
Copy link

MicBe commented Dec 14, 2016

Hi,

First of all, great library! I have some questions about it.

I have classes with members like this:

class User
{
public:
    boost::optional<string> m_name;
    boost::optional<Mailbox> m_mailbox;
    boost::optional<vector<string>> m_notes;
}

I have declared those members as properties of the class and I can access them correctly.

I have two problems:
1- During the looping over the properties of User, how can I know which one is a boost::optional (without using type name, which is not portable)?
My solution for now is to add metadata manually to each boost::optional property to indicate this, but I have a feeling this might not be the best solution.

2- Before getting the underlying value of the boost::optional property, I need to call is_initialized() on the property(which is of type boost::optional) to make sure the value is valid (otherwise, it's undefined behavior). The problem is that in order to call that method, I need to have an instance of boost::optional which I can't because Type is not known until runtime.
Unless I do a ton of ifs for each type(in this case, one for string, one for Mailbox and one for vector), I don't see how I can call is_initialized() for all types without typing them in the source code like this:

rttr::instance u = User();
for(const auto& property : object.get_properties())
{
    if(!property.get_metadata(IsOptionalKey).to_bool())
        continue;

    const auto& type = property.get_metadata(UnderlyingOptionalTypeKey);
    const rttr::type& underlyingOptionalType = type.get_value<rttr::type>();

    if(underlyingOptionalType == rttr::type::get<string>())
    {
        const boost::optional<string>& val = property.get_value(u).get_value<boost::optional<string>>();
        if(val.is_initialized()) { /* ... */ }
    }
    else if(underlyingOptionalType == rttr::type::get<Mailbox>())
    {
        const boost::optional<Mailbox>& val = property.get_value(u).get_value<boost::optional<Mailbox>>();
        if(val.is_initialized()) { /* ... */ }
    }
}

Is there something I'm missing or there is no easy solution to this?

@acki-m
Copy link
Contributor

acki-m commented Dec 18, 2016

Thank you for using RTTR.

Regarding the question of the naming you can set the name via
rttr::registration::class_, then this name is valid cross platform.
However, you can also check the type itself via the type::get()

(prop.get_type() == type::get<boost::optional<std::string>>()

boost::optional is for my point of view a wrapper, so you can provide a wrapper mapper:
http://www.rttr.org/doc/master/structrttr_1_1wrapper__mapper.html
That will allow you to extract the type directly via a variant method.

RTTR_REGISTRATION
{
    rttr::registration::class_<boost::optional<std::string>>("boost::optional<std::string>");
}

namespace rttr
{
    template<typename T>
    struct wrapper_mapper<boost::optional<T>>
    {
        using wrapped_type  = const T&;
        using type          = boost::optional<T>;

        static RTTR_INLINE const T& get(const type& obj)
        {
            return obj.get();
        }

        static RTTR_INLINE type create(const wrapped_type& t)
        {
            return type(t);
        }
    };
}

int main()
{
    auto u = User();
    u.m_name = std::string("some text");
    for (const auto& prop : type::get(u).get_properties())
    {
        if (prop.get_type() == type::get<boost::optional<std::string>>())
        {
            variant value = prop.get_value(u);
            // extract directly
            auto& text = value.get_wrapped_value<std::string>();
        }
    }
}

Regarding 2)
I have no solution for this right now. I tried register the is_initialized method, however its not possible to invoke this method, because is_initialized()is a method of a base class, thats why you have to place theRTTR_ENABLE` macro inside the class. Which you of course cannot...

@MicBe
Copy link
Author

MicBe commented Dec 22, 2016

Thanks for your answer. I can't use the wrapper_mapper unfortunately because at some point, some code seems to call get() when the value is not initialized. But I can use a succession of ifs, I don't have too many types anyway.
Thanks

@acki-m
Copy link
Contributor

acki-m commented Jan 1, 2017

I thinking there is a way to register the "is_initialized" method from the base class and invoke it, when you have a derived instance. So you don't need to create your own optional class. However, I need to prototype this first, whether it works and the performance implication.
@MicBe
Remark that you can no register the class template, i.e. std::vector<T>, however, you can register the instantiated template, e.g.: std::vector<int>.

@MicBe
Copy link
Author

MicBe commented Jan 3, 2017

Thanks, indeed, registering the "is_initialized" method would be a much better solution!

@acki-m
Copy link
Contributor

acki-m commented Mar 31, 2017

@MicBe
I was finally able to implement a proper solution to this problem.
Now you can register your boost optional::is_initialized method and it will be invoked.
There is actually now overhead with my current solution, I implicit register the base class during registration of the method/property.

This is actually a pretty nice feature, thanks for the suggestion. 👍

I will merge it now to master

@acki-m acki-m closed this as completed in 5a90984 Mar 31, 2017
@MicBe
Copy link
Author

MicBe commented Apr 5, 2017 via email

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

No branches or pull requests

2 participants