如何在 C++ 中以元组形式获取构造函数参数类型?

问题描述 投票:0回答:1

无法检索元组中的构造函数参数。

我正在尝试将给定类的构造函数参数类型提取为

std::tuple
。例如,如果我有一堂这样的课:

struct MyClass {
    MyClass(int, double, std::string) {}
};

我想获取像

std::tuple<int, double, std::string>
这样的类型对应构造函数的参数。

我知道在 Boost.DI 中,这似乎是可以实现的,因为它们设法自动解决构造函数依赖关系。但是,我找不到关于他们如何提取构造函数的参数类型的明确解释。

对于常规功能,我知道如何将

std::tuple
与模板一起使用,如下所示:

template <typename Func>
struct function_traits;

// Specialization for functions
template <typename Ret, typename... Args>
struct function_traits<Ret(Args...)> {
    using args_tuple = std::tuple<Args...>;
};

但是,我不确定如何为构造函数采用这种方法。

限制:

  • 我更喜欢与 C++14 或 C++17 兼容的解决方案,但我愿意看到使用 C++20 或更高版本的方法。
  • 如有必要,我不反对使用 Boost 或其他库。

有人可以指导我如何实现这一目标吗?也许是一个最小的例子或解释 Boost.DI 如何做到这一点?

如果有多个构造函数,boost di 表示它会选择最长的构造函数。它也是怎么做到的?

boost di 的工作原理示例(来自官方文档)。 参考:增强示例

// $CXX -std=c++14 basic_create_objects_tree.cpp
#include <boost/di.hpp>

namespace di = boost::di;

struct renderer {
  int device;
};

class view {
 public:
  view(std::string /*title*/, const renderer&) {}
};

class model {};

class controller {
 public:
  controller(model&, view&) {}
};

class user {};

class app {
 public:
  app(controller&, user&) {}
};

int main() {
  /**
   * renderer renderer_;
   * view view_{"", renderer_};
   * model model_;
   * controller controller_{model_, view_};
   * user user_;
   * app app_{controller_, user_};
   */

  auto injector = di::make_injector();
  injector.create<app>();
}

我想我有使用这个文件的线索,但我仍然无法完全理解该系统以及如何实现它。 boost/di/type_traits/ctor_traits.hpp

c++ dependency-injection constructor metaprogramming sfinae
1个回答
0
投票

首先,计算它们有多少个参数(使用 Ted Lyngmo 的代码)(带有硬编码限制):

template<class T, std::size_t I>
struct Any {
    template<class U>
    operator U() const;
};

template <class T, size_t N>
struct is_valid_amount {
    static std::false_type test(...);

    template <std::size_t... Is>
    static auto test(std::index_sequence<Is...>)
        -> decltype(T(Any<T, Is>{}...), std::true_type{});

    static constexpr bool value =
        decltype(test(std::make_index_sequence<N>{}))::value;
};
template <class T, size_t N>
inline constexpr bool is_valid_amount_v = is_valid_amount<T, N>::value;

template<class T, std::size_t N>
struct ctor_def : std::integral_constant<std::size_t, N> {
};

template <class T, std::size_t N = 16> // increase max amount of args if you wish
struct ctor_arg_count
    : std::conditional_t<is_valid_amount_v<T, N>,
                        ctor_def<T, N>,
                        ctor_arg_count<T, N - 1>> {};
template<class T>
inline constexpr std::size_t ctor_arg_count_v = ctor_arg_count<T>::value;

然后,通过有状态元编程,您可能会这样做:

namespace details
{
   template<std::size_t N> struct tag{};

    template<typename T, std::size_t N>
    struct loophole_t {
        friend auto loophole(details::tag<N>) { return std::type_identity<T>{}; };
    };

    auto loophole(tag<0>);
    auto loophole(tag<1>);
    auto loophole(tag<2>);
    auto loophole(tag<3>);
    auto loophole(tag<4>);
    auto loophole(tag<5>);
    auto loophole(tag<6>);
    auto loophole(tag<7>);
    auto loophole(tag<8>); // Add extra

    template <std::size_t N>
    struct detector {
        template <class T, std::size_t = sizeof(details::loophole_t<T, N>)>
        operator T();
    };

    template<typename T, typename Seq> struct ins;

    template<typename T, std::size_t... Is>
    struct ins<T, std::index_sequence<Is...>>
    {
        template <std::size_t = sizeof(T{detector<Is>{}...})>
        constexpr int operator()() const { return 0; }
    };

    template <std::size_t, std::size_t... Is, typename T = std::tuple<typename decltype(loophole(details::tag<Is>{}))::type...>>
    T get_type_impl(std::index_sequence<Is...>);

    template <typename T, std::size_t N = details::ctor_arg_count_v<T>>
    auto get_type()
    {
        return details::get_type_impl<details::ins<T, std::make_index_sequence<N>>{}()>(std::make_index_sequence<N>());
    }

}

template <typename T>
using constructor_type_as_tuple_t = decltype(details::get_type<T>());

演示

© www.soinside.com 2019 - 2024. All rights reserved.