0

This one is rather complex, so I haven't been able to solve it myself.

Here's the relevant code, I'll explain more in depth after.

#include <memory>
#include <vector>
#include <tuple>
#include <typeinfo>
#include <iostream>

struct Prop
{
    virtual ~Prop() {};
};

struct First : Prop
{
    int a;
};

struct Second : Prop
{
    int b;
};

struct Third : Prop 
{
    int c;
};


class PropManager
{
public:
    template<typename PropType>
    static std::shared_ptr<PropType> AddProp()
    {
        auto prop = std::make_shared<PropType>();
        props.push_back(prop);
        return prop;
    }

    static std::vector<std::shared_ptr<Prop>> props;

    template <typename PropType>
    static std::vector<std::shared_ptr<PropType>> GetProps()
    {
        std::vector<std::shared_ptr<PropType>> propTypes;
        for (std::shared_ptr<Prop> prop : props)
        {
            if (!prop) continue;
            if (typeid(PropType) == typeid( *prop.get() ) )
            {
                propTypes.push_back(std::static_pointer_cast<PropType>(prop));
            }
        }
        return propTypes;
    }

private:
    template <typename NthPropType, typename ...RemainingPropTypes>
    static void
    RecurseFillPropTuples
    (
        std::vector<std::tuple<std::shared_ptr<NthPropType>, std::shared_ptr<RemainingPropTypes>... >>* tuples,
        std::size_t recurse_count
    )
    {
        auto props = GetProps<NthPropType>();
        int i = 0;
        for (std::shared_ptr<NthPropType> prop : props)
        {
            std::get<recurse_count>( (*tuples)[i] ) = prop;
            i++;
        }
        if (sizeof...(RemainingPropTypes) > 0) {
            RecurseFillPropTuples<RemainingPropTypes...>(tuples, recurse_count + 1);
        }
    }

public:
    template <typename FirstPropType, typename ...NextPropTypes>
    static std::vector<std::tuple<std::shared_ptr<FirstPropType>, std::shared_ptr<NextPropTypes>... >>*
    GetPropTuples
    (
        std::vector<std::tuple<std::shared_ptr<FirstPropType>, std::shared_ptr<NextPropTypes>... >>* tuples = nullptr,
        std::size_t recurse_count = 0
    )
    {
        auto firstPropVector = GetProps<FirstPropType>();
        tuples = new std::vector<std::tuple<std::shared_ptr<FirstPropType>, std::shared_ptr<NextPropTypes>... >>(firstPropVector.size());

        int i = 0;
        for (std::shared_ptr<FirstPropType> prop : firstPropVector)
        {
            std::get<0>((*tuples)[i]) = prop;
            i++;
        }

        if (sizeof...(NextPropTypes) > 0)
        {
            PropManager::RecurseFillPropTuples<FirstPropType, NextPropTypes...>(tuples, recurse_count + 1);
        }
        return tuples;
    }
};

std::vector<std::shared_ptr<Prop>> PropManager::props = {};

int main()
{
    PropManager::AddProp<First>();
    PropManager::AddProp<Second>();
    PropManager::AddProp<Third>();

    PropManager::GetPropTuples<First, Second, Third>();
}

Ultimately, my desire is to return a vector of tuples of templated types. There are actually two related problems going on here.

PropManager::RecurseFillPropTuples<FirstPropType, NextPropTypes...>(tuples, recurse_count + 1);
  1. I need need to pass all types rather than folding because argument tuples requires all types to be known at each recursion call
std::get<recurse_count>( (*tuples)[i] ) = prop;
  1. std::get/std::tuple_element require a constexpr index parameter, so I cannot iterate through tuples types.
  • 2
    The shown code in this question fails to meet stackoverflow.com's requirements for a [mre], and that makes it pretty much impossible for anyone to try different approaches, themselves, in order to make sure they came up with a working solution. This question must be [edit]ed to show a minimal example, no more than one or two pages of code (the "minimal" part), that anyone can cut/paste, compile, run, and reproduce the described issue (the "reproducible" part) ***exactly as shown***. See [ask] for more information. – Sam Varshavchik Mar 14 '20 at 02:37
  • @SamVarshavchik I edited the code to make it reproducible :) – Wesley Barlow Mar 14 '20 at 03:07
  • Is there a reason for using `std::shared_ptr` instead of `std::unique_ptr` with non-owning pointers? From what I can tell, the `PropManager` owns `Prop`s so there doesn't seem to be any reason for the ownership to be shared. Also, could you explain what `GetPropTuples` is meant to do? I've read it many times and I'm still not sure. I think what you're looking for might be `std::index_sequence`. There are some odd things in the code. Could you explain what `PropManager` is for? With that information, someone might be able to design something simpler. – Indiana Kernick Mar 14 '20 at 04:29
  • 1
    What if the numbers of different props don't agree? e.g., what do you return if we have 1 `First`, 2 `Second`s, and 3 `Third`s? – L. F. Mar 14 '20 at 06:52
  • @L.F. I tried to not include any code related to determining which prop tuples overlap for simplicity, but this shouldn't be a problem – Wesley Barlow Mar 14 '20 at 19:04

1 Answers1

0

First point: as pointed by L.F., you set the size of the tuple allocated in GetPropTuples() as the number of FirstPropType in props. What if the number of elements of the following types is bigger?

    auto firstPropVector = GetProps<FirstPropType>();
    tuples = new std::vector<std::tuple<std::shared_ptr<FirstPropType>, std::shared_ptr<NextPropTypes>... >>(firstPropVector.size());

Given that I leave this problem unresolved, I suggest you to avoid recursion and, given that you've tagged C++17, the use of folding.

Other suggestion: use auto when possible.

So, given an helper function that set the values, given the type and the corresponding index

  template <std::size_t I, typename PType, typename VType>
  static void SetTuples (VType * pv)
   {
     std::size_t ind{};

     for ( auto prop : GetProps<PType>() )
        std::get<I>( (*pv)[ind++] ) = prop;
   }

you, substantially, only need a index sequence, so

  template <typename ... PTypes, std::size_t ... Is>
  static auto GetPropTuples (std::index_sequence<Is...>)
   {
     using RetType
        = std::vector<std::tuple<std::shared_ptr<PTypes>...>>;

     auto tuples = new RetType(1u); // <<--- set the correct size!!!

     (SetTuples<Is, PTypes>(tuples), ...);

     return tuples;
   }

  template <typename ... PTypes>
  static auto GetPropTuples ()
   { return GetPropTuples<PTypes...>
        (std::index_sequence_for<PTypes...>{}); }
max66
  • 65,235
  • 10
  • 71
  • 111