1
\$\begingroup\$

I'm exploring the possibility to generalise a class of mine that stores some entities. I like it to be able to keep entities of several type, given as template arguments. To explore the possibilities I wrote the following class, that does exactly that. I'm however unsure if anything that I do here is a) save and b) as easy as it gets. I'm not very deep into metaprograming. I'm looking for tips how to improve and pitfalls.

#include <iostream>
#include <tuple>
#include <vector>

template<typename... Ts>
class A
{
    template<typename T>
    using container = std::vector<T>;

public:
    template<typename T>
    void add(T e) {
        std::get<container<T>>(m_tupel).emplace_back(e);
    }

    template<typename T>
    container<T>& get() {
        return std::get<container<T>>(m_tupel);
    }

private:
    std::tuple<container<Ts>...> m_tupel;
};

int main() 
{ 
    A<int, bool, double, float> a;
    a.add(3);
    a.add(33);
    a.add(3.14);
    a.add(333);
    a.add(31.4);

    const auto& cDouble = a.get<double>();
    const auto& cInt = a.get<int>();

    for(auto i : cDouble)
        std::cout<<i<<" ";
    std::cout<<"\n";
    for(auto i : cInt)
        std::cout<<i<<" ";
}
\$\endgroup\$

closed as off-topic by Stephen Rauch, yuri, Snowhawk, t3chb0t, Mast Jul 30 '18 at 10:01

This question appears to be off-topic. The users who voted to close gave this specific reason:

  • "Lacks concrete context: Code Review requires concrete code from a project, with sufficient context for reviewers to understand how that code is used. Pseudocode, stub code, hypothetical code, obfuscated code, and generic best practices are outside the scope of this site." – Stephen Rauch, yuri, Snowhawk, t3chb0t, Mast
If this question can be reworded to fit the rules in the help center, please edit the question.

  • \$\begingroup\$ May be you wanted a vector of std::variants? Or even std::anys? Though the latter is great footshooter. \$\endgroup\$ – Incomputable Aug 8 '17 at 17:09
  • \$\begingroup\$ Using variant/any is good when the types are only known at runtime. tuple is better when the types are known at compile time. \$\endgroup\$ – Martin York Aug 8 '17 at 19:51
  • \$\begingroup\$ Why do you have a tuple of vector<T>? \$\endgroup\$ – Martin York Aug 8 '17 at 19:56
5
\$\begingroup\$

A few points of criticism of your approach:

Prefer protected to private

This isn't really about your overall design, but - unless you have very good reason, use protected rather than private member. In few cases is it useful to hide data from your subclasses.

"Where did my data go?!"

Suppose you have vectors types T_1 and T_2 in your tuple, and you add() an element of type T_3, which may be implicitly cast into both a T_1 and a T_2. Where will it go? The answer is: It depends on which casting is preferred, which in turns depends both on language defaults and on your own choices of constructors and casting operations for T_1, T_2 and T_3.

Do you remember with certainty which implicit casts are preferred over which? I know I don't, and I write C++ all the time. Now, ok, maybe you're a smart guy and have that all memorized. What about the next person reading your code? And what if it's in code which itself is templated? And what if somebody adds or removes some T_1, T_2 or T_3 code? It seems to me this is just asking for trouble.

If your data goes in a vector other than the one you expect, you'll have to start searching vectors you have trouble even referring to, for that data.

So, you asked about what's "safe" - I would say this is not too safe.

Motivation

This is not a problem with the code, it's a problem with your presentation. Why would you need a tuple of vectors of arbitrary types? Are you really sure that's what you need? Not, say, an array of std::variants like one of the comments suggests? Or a class with named vector, e.g.

struct A {
    std::vector<Foo> foos;
    std::vector<Bar> bars;
}

Now, this is much less "fancy" and "hi-tech" and modern-C++'ish, but it is simpler and easier to debug. There are also other points in the design space. I just find it a bit hard to believe that you would really need a completely generic tuple of typed vectors.

\$\endgroup\$
1
\$\begingroup\$

I used a very similar approach in an ECS game engine I developed. It worked well for what it did, which was separate entities by type to allow a more data driven approach and thereby improve performance. Eventually, however, I chose to move even further down a meta-programming path by having the systems provide a method to determine if the EntityType met the criteria for use by the System and a vector each System provided to store all entities of those types. I chose this, rather than to separate by type which is more of an OO way of thinking to avoid forcing entities to support a base class to be used by a system. I had an EntityManager with an AddEntityType function which called the AddEntityType function for each System which would only add the EntityType if it met the criteria.

There is a lot of use for such strategies when working with an open ended set of EntityTypes, particularly when designing libraries for use by other programmers. Understanding Template Metaprogramming is a key skill in C++ today and in moving from the OO to Functional approach which I believe provides many dividends in developing type safe multi-threaded code, especially for large systems. I think you are on the right path in your learning. I am on the same path with you. It is very challenging but I expect it to be well worth it. Keep up the good work.

\$\endgroup\$

Not the answer you're looking for? Browse other questions tagged or ask your own question.