Как стать автором
Обновить

Комментарии 11

В C++11 есть variadic templates, разве нельзя их использовать для подобных целей? Или std::initializer_list, если надо задавать параметры в runtime.

Не могу понять, что у вас за постановка задачи, что обычные плюсовые лямбды не подошли в качестве делегатов. Не оптимально, но всяко лучше, чем использовать std::any или RTTI.

Я возможно что то не так понял, но чем это отличается от std::bind?

Появились ли какие-то средства C++ которые позволяют удобно выполнять динамические вызовы IDispatch::Invoke и реализовывать их.
Существует много реализаций делегатов для C++, я бы выделил The Impossibly Fast C++ Delegates и его усовершенствованный вариант. Данная реализация обладает не совсем приятным синтаксисом, но зато имеет минимальные накладные расходы.
Я не скажу, что глубоко вникал, но показалось, что и вызываемая функция, и ее возвращаемый тип, и аргументы задаются во время компиляции. Получается обычный std::function или boost::binder, какое отношение это имеет к «как реализовать некоторые средства рефлексии и динамического вызова функций в C++»? Чистый compile time, как упаковать произвольный вызов в экземпляр класса известно с 1998 года, когда вышел первый стандарт C++.
И маленькое замечание: имена вида _Tagged_args_binder осуждаются стандартом. Ваш любимый язык действительно C++ а не C#?

Просто он много смотрел в исходники стандартной библиотеки, где такие имена используются повсеместно :-)

Это лишь часть реализации библиотеки делегатов. С помощью этих средств я решил проблему со связыванием аргументов из std::vector'а с функцией с произвольными параметрами и возвращаемым значением. Это лишь шаг на пути к разработке библиотеки делегатов.
Да, верно, параметры и возвращаемые значения, конечно, задаются статически во время компиляции. Но суть механизма, который я назвал «динамическим вызовом функций» — в том, чтобы получить объект делегата, а потом вызвать его, возможно, не зная ничего о параметрах и возвращаемом значении функции, на которую ссылается делегат. Там будет виртуальный метод, принимающий std::vector<std::any>, а с помощью Variadic_args_binder внутри метода будет происходить связывание аргументов и затем вызов функций. Таким образом я пытаюсь осуществить «стирание типов» («type erasure»), тогда делегаты станут нетипизированными, что непосредственно средствами языка в C++ сделать очень непросто.
Действительно, идентификаторы, начинающиеся с одинарного подчёркивания и следующей за ним заглавной буквы, зарезервированы стандартом C++ для реализации языка. Я тем самым пытаюсь показать пользователю, что использовать этот класс напрямую не стоит, так как это будет очень «грязно». Для этого есть класс-обёртка. А совпадение полного имени этого класса (с учётом пространств имён) едва ли возможно. Ведь библиотека будет выделена в отдельное пространство имён.
И да, мой любимый язык — действительно C++.
«Делегат», как я понимаю, не имеет отношение к C++. В C++ это называется callable object, зафиксировано в стандарте и является first class citizen. Зачем придумывать какие-то «делегаты» в C++? Они принадлежат C#, где было надо как-то назвать тип для «вызывабельных» переменных.
Я тем самым пытаюсь показать пользователю, что использовать этот класс напрямую не стоит, так как это будет очень «грязно».

Мне кажется, что освоение шаблонов с переменным числом аргументов — недостаточное основание для того, чтобы ставить себя вровень со стандартом языка C++.
О, куда там мне до того, чтобы быть вровень со стандартом!
Возможно, вы неправильно меня поняли, я не пытаюсь показать, что стою на одном уровне со стандартом языка. И, да, в C++ лучше называть объекты, которые можно вызывать, «функторами» или «функциональными объектами». Я назвал это «делегатами», чтобы вызвать ассоциации с делегатами из .NET, потому что хочу реализовать что-то подобное. Но стоит ли цепляться за терминологию?
К тому же, я не претендую на то, что эта разработка будет полезна где-нибудь в реальном коде (хотя, это не исключено). Однако, это ведь не повод не заниматься тем, что тебе интересно?

Отталкиваясь от примера, не вижу где здесь делегаты. Мне кажется, вы смешиваете несколько проблем в одну. Если вам просто нужно превратить массив any в tuple<>, так и сделайте. Имея такую основу, всё становится проще. Вот несколько строчек:


#include <vector>
#include <any>
#include <tuple>

template<typename... Args>
struct Binder
{
    static std::tuple<Args...> convert_args(std::vector<std::any> any_args)
    {
        auto convert = [i = 0u, &any_args](auto& arg) mutable
        {
            arg = std::any_cast<decltype(arg)>(any_args.at(i));
            ++i;
        };
        std::tuple<Args...> vs;
        std::apply([&](Args& ... args) { (convert(args), ...); }, vs);
        return vs;
    }

    template<typename F>
    decltype(auto) call(F f, std::vector<std::any> args) const
    {
        return std::apply(std::move(f), convert_args(std::move(args)));
    }
};

#include <string>
#include <iostream>

int f(int a, std::string s)
{
    std::cout << "int: " << a << "\nstring: " << s << std::endl;
    return 1;
}

int main()
{
    return Binder<int, std::string>().call(
        f, {5, std::string("Hello, Delegates!")});
}
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории