满足某个概念的所有完整类型的元组

Tuple of all complete types satisfying a certain concept

我想获取满足某个概念的所有完整类型。类型应列为 std::tuple.

的元素类型

对于:

#include <concepts>
#include <tuple>

class A{};

template<typename T>
concept InterestingType = std::derived_from<T, A>;

class B : public A{};
class C : public A{};
class D{};

我想获得类型 std::tuple<A, B, C>(因为 ABC 满足概念 InterestingTypeD没有。)。元组中 classes 的顺序无关紧要(例如 std::tuple<C, B, A> 也可以)。重复可能也很好(例如 std::tuple<A, B, C, A, A>)。

这可能吗?

更新: 正如评论中所问,这里是回答这个问题的计划目的:

我有许多预定义的 classes,它们有一个字符串标识符并提供一个解释字符串的解析构造函数,例如:

struct MyInt {
static constexpr char name[] = "MyInt";
int i_;
MyInt(std::string);
}
struct MyDouble {
static constexpr char name[] = "MyDouble";
double d_;
MyInt(std::string);
}

还有一个工厂方法:

std::any make(std::string type_name, std::string constructor_arg){
// generated code
if (type_name == MyInt::name)
  return MyInt{constructor_arg};
else if (type_name == MyDouble::name)
  return MyDouble{constructor_arg};
else 
  return {};
}

make 实际上调用了一个编译时生成的类似 switch-case 的模板函数,该函数将类型元组(即 std::tuple<MyInt, MyDouble>)作为模板参数并生成与上面提供的实现等效的代码. 我目前手动定义注册类型的元组。 这对于库附带的预定义类型是可以的。

我正在努力解决的(以及为什么我问第一个问题)是用户如何提供额外的类型并注册它们。

最后说明: 虚拟构造函数由于其他技术原因没有选择。 已注册的类型(如 MyIntMyDouble) 由于其他技术原因不能有虚拟方法。

更新2:

我用下面 HolyBlackCat 的回答中的代码构建了一个工作示例。它在 clang >= 10 下编译得很好,但在所有 gcc 版本上都失败了。结帐 https://godbolt.org/z/9xxbo79rr

知道我需要更改什么才能使用 gcc 构建吗?至少gcc 11应该可以编译。

#include <iostream>

#include <any>
#include <string>
#include <map>

using func_ptr = std::any (*)(std::string);
using func_map = std::map<std::string, func_ptr>;

struct Factory {
    inline static func_map &make_funcs() {
        static func_map func_ptrs;
        return func_ptrs;
    }
};

template<typename T>
struct Base {
    inline static const std::nullptr_t dummy = [] {
        // This code will run for each derived class, when the program starts.

        Factory::make_funcs()[T::name] = [](std::string s) -> std::any { return T{std::move(s)}; };
        return nullptr;
    }();

    // Force `dummy` to be instantiated, even though it's unused.
    static constexpr std::integral_constant<decltype(&dummy), &dummy> dummy_helper{};
};

struct MyInt : Base<MyInt> {
    static constexpr char name[] = "MyInt";
    int i_ = 5;

    MyInt(std::string) {}
};

struct MyDouble : Base<MyDouble> {
    static constexpr char name[] = "MyDouble";
    double d_ = 4.0;

    MyDouble(std::string) {}
};


int main() {
    for (auto &[_, make] : Factory::make_funcs()) {
        auto instance = make("a");
        try {
            any_cast<MyDouble>(instance);
            std::cout << "is MyDouble" << std::endl;
        } catch (...) {}

        try {
            any_cast<MyInt>(instance);
            std::cout << "is MyInt" << std::endl;
        } catch (...) {}
    }
}

更新 3:

它的工作原理是使 dummy 的 init 函数成为 class 中未定义的单独函数。参见 https://godbolt.org/z/hnW58zc4s

#include <iostream>

#include <any>
#include <string>
#include <map>

using func_ptr = std::any (*)(std::string);
using func_map = std::map<std::string, func_ptr>;

struct Factory {
    inline static func_map &make_funcs() {
        static func_map func_ptrs;
        return func_ptrs;
    }
};

template<typename T>
struct Base {
    inline static std::nullptr_t init();
    inline static const std::nullptr_t dummy = init();

    // Force `dummy` to be instantiated, even though it's unused.
    static constexpr std::integral_constant<decltype(&dummy), &dummy> dummy_helper{};
};

template<typename T>
std::nullptr_t Base<T>::init() {
    Factory::make_funcs()[T::name] = [](std::string s) -> std::any { return T{std::move(s)}; };
    return nullptr;
}

struct MyInt : Base<MyInt> {
    static constexpr char name[] = "MyInt";
    int i_ = 5;

    MyInt(std::string) {}
};

struct MyDouble : Base<MyDouble> {
    static constexpr char name[] = "MyDouble";
    double d_ = 4.0;

    MyDouble(std::string) {}
};


int main() {
    for (auto &[_, make] : Factory::make_funcs()) {
        auto instance = make("a");
        try {
            any_cast<MyDouble>(instance);
            std::cout << "is MyDouble" << std::endl;
        } catch (...) {}

        try {
            any_cast<MyInt>(instance);
            std::cout << "is MyInt" << std::endl;
        } catch (...) {}
    }
}

获得“满足特定概念的所有完整类型”是不可能的(至少在我们得到反思之前),所以我正在回答问题的第二部分。


第 1 步:您可能想使用 this 之类的东西来获取类型名称,这样您就不需要 classes 中的 name 变量。

第 2 步:制作一个 CRTP 基础,并从中继承您的 classes:

template <typename T> struct Base {};

struct MyInt : Base<MyInt> {};
struct MyDouble : Base<MyDouble> {};

第三步:将class注册码注入基地:

template <typename T>
class Base
{
    static std::nullptr_t Register()
    {
        // This function will run for each derived class, when the program starts.

        std::ios_base::Init init; // Without this, `std::cout` might not work this early.
        std::cout << "Hello, world!\n";
        return nullptr;
    }

    inline static const std::nullptr_t dummy = Register();

    // Force `dummy` to be instantiated, even though it's unused.
    static constexpr std::integral_constant<decltype(&dummy), &dummy> dummy_helper{};
};

此代码将 运行 当您的程序启动时,在输入 main 之前,对于从 Base<...>.

派生的每个 class

第 4 步:创建一个单例,类似于 std::map<std::string, std::any(*)()>,以存储构造每个 class 的函数。从 class Base 中的静态变量初始值设定项填充它,如上所示。

确保单例是函数中的一个 static 变量,而不是全局变量。否则,您将遇到静态初始化命令的惨败。