Как получить подпись вызываемого типа? - PullRequest
0 голосов
/ 15 февраля 2019

Я хочу создать красивый современный интерфейс для построения дерева вычислений, что-то вроде этого:

auto [F, G] = calcs.emplace(
        [](int a, int b){ return a + b; },
        [](){ return 4; }
    );

Я получил вдохновение от taskflow , но здесь мы могли бы добавить аргументыи возвращают типы, и здесь возникает проблема: как мы можем определить тип базового хранилища для заданных вызываемых объектов и как сохранить их в коллекции?Можно ли вообще создать такой простой API с текущими языковыми функциями?

Я гуглил это часами, и похоже, что возвращаемый тип - это меньшая проблема, но я не имею представления об аргументах.

1 Ответ

0 голосов
/ 17 февраля 2019

Q: Как получить подпись?

A: Путем сопоставления с шаблоном по operator() методу вызова, например:

template <class TMethodType>
struct ReadSignature;

// Const specialization
template <class TReturnType, class TClass, class ... TArgsTypes>
struct ReadSignature<TReturnType(TClass::*)(TArgsTypes...) const> {
    using ReturnType = TReturnType;
    using Class = TClass;
    using Args = std::tuple<TArgsTypes...>;

    static constexpr bool is_const = true;
};

// Non-const specialization
// This is for mutable lambdas, e.g. []() mutable {}
template <class TReturnType, class TClass, class ... TArgsTypes>
struct ReadSignature<TReturnType(TClass::*)(TArgsTypes...)> {
    using ReturnType = TReturnType;
    using Class = TClass;
    using Args = std::tuple<TArgsTypes...>;

    static constexpr bool is_const = false;
};

Вы используете это так:

    auto callable = [](int x, int y) { return x + y; };
    using Class = decltype(callable);
    using Signature = ReadSignature<decltype(&Class::operator())>;

Q: Как хранить вызываемые элементы в коллекции?

A: ВыНужно будет стереть типы так или иначе.Для вызываемых объектов создание интерфейса-обертки кажется естественным.Например, что-то вроде этого:

class CallableI {
    virtual ~CallableI() = default;

    virtual std::any call(const std::vector<std::any>& args) = 0;

    // For type checking:
    virtual size_t arg_count() = 0;
    virtual std::type_info get_arg_type_info(size_t arg_index) = 0;
    virtual std::type_info get_return_type_info() = 0;
};

Затем вы пишете шаблонный класс, реализующий этот интерфейс, который будет создан для каждого лямбда-аргумента.В вашем calcs объекте вы на самом деле храните std::vector<std::unique_ptr<CallableI>>.

...