简体   繁体   中英

C++ how to iterate through variadic template types and add them to tuple?

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.

First point: as pointed by LF, 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...>{}); }

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