Создание диспетчера лямбда-функций

Я подумываю о создании функции dispatcher() для сервера, указав пары {ID, LAMBDA} для сервера на выбор. Все идентификаторы уникальны. Предположим, у меня есть функции send() и receive(), поддерживающие все разрешенные типы. Как бы вы написали функцию dispatcher(), которая получает идентификатор, выбирает соответствующую лямбда-функцию, получает ее параметры, вызывает ее и отправляет результат? У меня будет несколько экземпляров dispatcher() для разных экземпляров сервера, каждый с разными наборами функций, и мне не нужны операторы переключения в реализации. Вот упрощенный скелет такого приложения (демо-версия компилятора):

#include <utility>
#include <vector>

template<class T>
auto receive() { return -1; } // for exposition only

void send(auto&& ... values);

template<std::pair...functions>
void dispatcher()
{
    for(auto function_id = 0; function_id != -1;)
    {
        function_id = receive<int>();
        // select function corresponding to function_id
        // receive function arguments
        // invoke function and send return value
    }
}

int main()
{
    dispatcher<{100, []{ return 100;}},
    {1000, [](const std::vector<int>& v){ return v; }}>();
}

Обычно этот вид диспетчеризации во время выполнения обрабатывается с использованием std::(unordered_)map идентификаторов, сопоставленных с указателями на функции (или лямбда-выражениями, хранящимися как std::function). Но функции/лямбды должны иметь общую подпись, если только вы не обернете их std::variant или эквивалентом. Я бы не стал пытаться сделать это, используя аргументы шаблона в диспетчере.

Remy Lebeau 07.08.2024 19:48

Похоже, кто-то пытается заново изобрести подмножество COM ... Примечание: его всегда изобретают плохо.

ShadowRanger 07.08.2024 19:51

Являются ли эти ID монотонно возрастающими int? (1, 2, 3, 4, ...)

catnip 07.08.2024 20:00

@catnip — я бы не хотел требовать заказа без необходимости.

Gene 07.08.2024 20:01

Ваши лямбды не принимают одни и те же аргументы, поэтому часть «получения аргументов функции» становится интересной. Откуда они берутся? Как они упакованы? В каком-то кортеже? Если все ваши лямбды принимают аргументы одного и того же типа, проблем нет.

Ted Lyngmo 07.08.2024 20:01

@gene В этом случае я бы согласился с предложением Реми, хотя Тед высказывает веское мнение.

catnip 07.08.2024 20:07

Возьмите и верните какой-нибудь вариант формата (например, json).

Jarod42 07.08.2024 20:43

@Тед Люнгмо - да, лямбды могут иметь разные аргументы. Конечно, их можно упаковать в кортежи и расширять при необходимости.

Gene 07.08.2024 22:09

@Gene Возможно, вам следует упаковать как принимающую, так и отправляющую лямбды вместе, чтобы не выполнять два ID поиска? Я сделал эту демо-версию только для приемника. Это похоже на то, что вам нужно?

Ted Lyngmo 07.08.2024 22:18

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

Gene 07.08.2024 22:26

@Gene Если вы свяжете три части (получение аргументов функции, вызов функции с ними и отправку результата) вместе, это станет менее проблематичным. Тогда каждая лямбда будет автономной, состоящей из всех трех частей, и не будет необходимости в специальном обращении.

Ted Lyngmo 07.08.2024 22:36

@Тед Люнгмо - у тебя есть пример? Если я вас правильно понимаю, вам нужен кортеж из всех трех, но тогда кортеж не является структурным типом.

Gene 07.08.2024 22:51

@Gene Нет, я имею в виду, что вы создаете пару идентификатора и лямбды. Внутри лямбды он извлекает аргументы, которые могут быть особенными для каждой лямбды, вызывает функцию с этими аргументами и отправляет результат, который также может быть разным для каждой лямбды.

Ted Lyngmo 07.08.2024 22:53
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
13
76
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Это решение основано на следующих двух шагах:

  • Сначала мы создаем выражение-свертку (предложенное @Ted Lyngmo), которое приводит к вызову функции executor(), соответствующей лямбда-выражению идентификатор функции.

  • Во-вторых, мы используем свойства лямбда-функции для определения типов параметров. необходимо получить эти параметры, передав их в соответствующую лямбда-функцию и отправку результата выполнения.

Следующий код демонстрирует этот подход (Демонстрация Compiler Explorer):

template<class Fn> struct lambda_traits;

template< class R, class G, class ... A >
struct lambda_traits<R(G::*)(A...) const>
{
    using Args = std::tuple<std::remove_cvref_t<A>...>;
};

template<class Fn>
struct lambda_traits : lambda_traits<decltype(&Fn::operator())>{};

template<class Fn>
using lambda_args = typename lambda_traits<std::remove_cvref_t<Fn>>::Args;

template <std::pair function>
auto executor() {
    // receive function arguments for function.first here
    std::cout << "Executing function ID " << function.first << '\n';
    
    std::apply([&](auto&&...args)
    {
        (receive(args), ...);
        send(std::invoke(function.second, std::forward<decltype(args)>(args)...));        
    }, lambda_args<decltype(function.second)>{});
}

template<std::pair...functions>
void dispatcher()
{
    for(auto function_id : {10, 100})
    {
        //function_id = receive<int>();
        (void)((functions.first == function_id
            &&
        (executor<functions>(), true)) || ...);
    }
}

int main()
{
    dispatcher<{100, []{ return 123;}},
    {10, [](const std::vector<int>& v){ return v; }}>();
}

Вам нужно обернуть receive<Args...>() в std::initializer_list (чтобы инициализировать кортеж), чтобы иметь гарантированный порядок вычислений.

Jarod42 08.08.2024 13:36

@Jarod42 - хороший улов, оператор запятая тоже это сделает.

Gene 08.08.2024 19:52

@Jarod42 - нарушает ли обработка ссылок rvalue как ссылок lvalue, пока существует временное значение, что-либо в Стандарте?

Gene 08.08.2024 20:09

Насколько я понимаю, ваш вопрос нет. Кстати, у них есть имя, есть l-значения.

Jarod42 09.08.2024 19:59

Другие вопросы по теме

Различные примечания для неинициализированного члена внутри общей лямбды в GCC 9 и GCC 10
Размышления C# о том, как вызвать метод статических расширений с параметром выражения лямбда
Сохраняет ли std::future лямбду std::async после выполнения?
Как я могу использовать лямбда-выражение в качестве постоянного основного выражения в предложении require?
C# LinQ: фильтровать объект[] с элементами, содержащими строку (или ее часть) из строки[]
Неточные вычисления последовательности Фибоначчи во время компиляции в рекурсивной лямбда-выражении
Ясность синтаксиса Java Lambda
В GCC внутри лямбды я могу получить переменную constexpr из лямбды шаблона, отличного от constexpr, но не в Visual C++
Получить переменную constexpr из лямбда-функции можно, но компиляция завершается неудачей (Visual C++) и штрафом (gcc), когда такой оператор находится в новой лямбда-функции
Каков тип шаблонной лямбды?