简体   繁体   中英

Automate repeated code for various types?

Is there is any way by which I can automate this

DeserializeComponent<IDComponent>(json, e);
DeserializeComponent<NameComponent>(json, e);
DeserializeComponent<PointLightComponent>(json, e);
// ...

As you can see here, the same code is executed for different types, but in C++ you can't store types in a std::vector as far as my knowledge goes. Is there is any way by which I can automate this? Like looping over the components that I add to a vector in application startup? Also, I want to avoid RTTI.

You can store your types in std::tuple<tag<Ts>...> or TypeList<Ts...> and then use variadic template.

template <typename T> struct Tag{ using type = T; };

using MyTypes = std::tuple<Tag<IDComponent>, Tag<NameComponent>/*, ...*/>;

// And then do something like
void foo()
{
    // ...
    apply([&](auto tag){ DeserializeComponent<typename decltype(tag)::type>(json, e) },
         MyTypes{});
}

Types can't be stored in variables. Types are only for the compiler. Even RTTI doesn't store types in variables, but rather "names" of types.

I think you just want to make the code shorter by not having to type DeserializeComponent<>(json, e); over and over. Well, you can do that with parameter pack expansion .

template<typename... Components>
void DeserializeComponents(json_t& json, e_t& e)
{
    (DeserializeComponent<Components>(json, e), ...);
}

// ...

DeserializeComponents<IDComponent, NameComponent, PointLightComponent>(json, e);

The magic is in typename... Components - which says Components is not just one type argument but a list of type arguments - and (DeserializeComponent<Components>(json, e), ...); which says to copy-paste the function call for each Components argument, and join them together with the comma operator ,

When the compiler expands the template, the expanded template looks like this:

void DeserializeComponents<IDComponent, NameComponent, PointLightComponent>(json_t& json, e_t& e)
{
    (
     DeserializeComponent<IDComponent>(json, e),
     DeserializeComponent<NameComponent>(json, e),
     DeserializeComponent<PointLightComponent>(json, e)
    );
}

You could also do it by wrapping the function call to DeserializeComponents<T>(...) inside a functor which recursively calls itself with the different types provided to it through a parameter pack .

// Other stuff...

// The code below works only with compilers that support C++11 or above
#include <utility>

template <typename T, typename ...Ts>
struct deserialize {
    template <typename A, typename B>
    void operator()(A&& a, B&& b) const {
        DeserializeComponent<T>(std::forward<A>(a), std::forward<B>(b));
        deserialize<Ts...>{}(std::forward<A>(a), std::forward<B>(b));
    }
};

template <typename T>
struct deserialize<T> {
    template <typename A, typename B>
    void operator()(A&& a, B&& b) const {
        DeserializeComponent<T>(std::forward<A>(a), std::forward<B>(b));
    }
};

// Other stuff...

Then you'd be able to do this:

deserialize<IDComponent, NameComponent, PointLightComponent/*, ...*/>{}(json, e);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM