Комментарии 11
В C++11 есть variadic templates, разве нельзя их использовать для подобных целей? Или std::initializer_list, если надо задавать параметры в runtime.
+2
Не могу понять, что у вас за постановка задачи, что обычные плюсовые лямбды не подошли в качестве делегатов. Не оптимально, но всяко лучше, чем использовать std::any или RTTI.
+3
Я возможно что то не так понял, но чем это отличается от std::bind?
+4
Появились ли какие-то средства C++ которые позволяют удобно выполнять динамические вызовы IDispatch::Invoke и реализовывать их.
0
Существует много реализаций делегатов для C++, я бы выделил The Impossibly Fast C++ Delegates и его усовершенствованный вариант. Данная реализация обладает не совсем приятным синтаксисом, но зато имеет минимальные накладные расходы.
0
Я не скажу, что глубоко вникал, но показалось, что и вызываемая функция, и ее возвращаемый тип, и аргументы задаются во время компиляции. Получается обычный std::function или boost::binder, какое отношение это имеет к «как реализовать некоторые средства рефлексии и динамического вызова функций в C++»? Чистый compile time, как упаковать произвольный вызов в экземпляр класса известно с 1998 года, когда вышел первый стандарт C++.
И маленькое замечание: имена вида _Tagged_args_binder осуждаются стандартом. Ваш любимый язык действительно C++ а не C#?
И маленькое замечание: имена вида _Tagged_args_binder осуждаются стандартом. Ваш любимый язык действительно C++ а не C#?
+4
Просто он много смотрел в исходники стандартной библиотеки, где такие имена используются повсеместно :-)
0
Это лишь часть реализации библиотеки делегатов. С помощью этих средств я решил проблему со связыванием аргументов из std::vector'а с функцией с произвольными параметрами и возвращаемым значением. Это лишь шаг на пути к разработке библиотеки делегатов.
Да, верно, параметры и возвращаемые значения, конечно, задаются статически во время компиляции. Но суть механизма, который я назвал «динамическим вызовом функций» — в том, чтобы получить объект делегата, а потом вызвать его, возможно, не зная ничего о параметрах и возвращаемом значении функции, на которую ссылается делегат. Там будет виртуальный метод, принимающий std::vector<std::any>, а с помощью Variadic_args_binder внутри метода будет происходить связывание аргументов и затем вызов функций. Таким образом я пытаюсь осуществить «стирание типов» («type erasure»), тогда делегаты станут нетипизированными, что непосредственно средствами языка в C++ сделать очень непросто.
Действительно, идентификаторы, начинающиеся с одинарного подчёркивания и следующей за ним заглавной буквы, зарезервированы стандартом C++ для реализации языка. Я тем самым пытаюсь показать пользователю, что использовать этот класс напрямую не стоит, так как это будет очень «грязно». Для этого есть класс-обёртка. А совпадение полного имени этого класса (с учётом пространств имён) едва ли возможно. Ведь библиотека будет выделена в отдельное пространство имён.
И да, мой любимый язык — действительно C++.
Да, верно, параметры и возвращаемые значения, конечно, задаются статически во время компиляции. Но суть механизма, который я назвал «динамическим вызовом функций» — в том, чтобы получить объект делегата, а потом вызвать его, возможно, не зная ничего о параметрах и возвращаемом значении функции, на которую ссылается делегат. Там будет виртуальный метод, принимающий std::vector<std::any>, а с помощью Variadic_args_binder внутри метода будет происходить связывание аргументов и затем вызов функций. Таким образом я пытаюсь осуществить «стирание типов» («type erasure»), тогда делегаты станут нетипизированными, что непосредственно средствами языка в C++ сделать очень непросто.
Действительно, идентификаторы, начинающиеся с одинарного подчёркивания и следующей за ним заглавной буквы, зарезервированы стандартом C++ для реализации языка. Я тем самым пытаюсь показать пользователю, что использовать этот класс напрямую не стоит, так как это будет очень «грязно». Для этого есть класс-обёртка. А совпадение полного имени этого класса (с учётом пространств имён) едва ли возможно. Ведь библиотека будет выделена в отдельное пространство имён.
И да, мой любимый язык — действительно C++.
0
«Делегат», как я понимаю, не имеет отношение к C++. В C++ это называется callable object, зафиксировано в стандарте и является first class citizen. Зачем придумывать какие-то «делегаты» в C++? Они принадлежат C#, где было надо как-то назвать тип для «вызывабельных» переменных.
Мне кажется, что освоение шаблонов с переменным числом аргументов — недостаточное основание для того, чтобы ставить себя вровень со стандартом языка C++.
Я тем самым пытаюсь показать пользователю, что использовать этот класс напрямую не стоит, так как это будет очень «грязно».
Мне кажется, что освоение шаблонов с переменным числом аргументов — недостаточное основание для того, чтобы ставить себя вровень со стандартом языка C++.
0
О, куда там мне до того, чтобы быть вровень со стандартом!
Возможно, вы неправильно меня поняли, я не пытаюсь показать, что стою на одном уровне со стандартом языка. И, да, в C++ лучше называть объекты, которые можно вызывать, «функторами» или «функциональными объектами». Я назвал это «делегатами», чтобы вызвать ассоциации с делегатами из .NET, потому что хочу реализовать что-то подобное. Но стоит ли цепляться за терминологию?
К тому же, я не претендую на то, что эта разработка будет полезна где-нибудь в реальном коде (хотя, это не исключено). Однако, это ведь не повод не заниматься тем, что тебе интересно?
Возможно, вы неправильно меня поняли, я не пытаюсь показать, что стою на одном уровне со стандартом языка. И, да, в C++ лучше называть объекты, которые можно вызывать, «функторами» или «функциональными объектами». Я назвал это «делегатами», чтобы вызвать ассоциации с делегатами из .NET, потому что хочу реализовать что-то подобное. Но стоит ли цепляться за терминологию?
К тому же, я не претендую на то, что эта разработка будет полезна где-нибудь в реальном коде (хотя, это не исключено). Однако, это ведь не повод не заниматься тем, что тебе интересно?
0
Отталкиваясь от примера, не вижу где здесь делегаты. Мне кажется, вы смешиваете несколько проблем в одну. Если вам просто нужно превратить массив 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!")});
}
+2
Зарегистрируйтесь на Хабре, чтобы оставить комментарий
Мой подход к реализации делегатов в C++: вызов функции с неизвестными параметрами во время выполнения