Настройка типа внешней переменной с использованием автоматических параметров лямбда

#c #templates #c 14 #auto

Вопрос:

Я использую веб-фреймворк C , который в значительной степени использует лямбда-функции обратного вызова. Как вы можете догадаться, параметры для лямбд обычно задаются как auto из-за необходимости объявлять очень длинные объявления.

Теперь я использую decltype() оператор для поиска правильного типа, выведенного с помощью auto , чтобы я мог объявить вектор того же типа. Когда объявление вектора происходит внутри лямбды, все в порядке.

Моя проблема начинается с того, что этот вектор должен быть объявлен во внешней области, используя auto информацию о типе параметров lambdas. Ниже приведен простой пример:

 std::vector<T> vec; // I want the type information to be inferred just like vec2 from lambda below
auto func = [](auto parameter){
    std::vector<decltype(parameter)> vec2; // No problem here.
};
 

Возможно ли это?

Обновить:

Структура, которую я использую, такова uWebSockets . Вот пример кода :

 using DataType = std::string;

// I had to go get type information from the source code.
static std::vector<uWS::WebSocket<false, true, DataType> *> users; 

uWS::App app {};

app.ws<DataType>("/*", {
    
    .open = [](auto * ws){
        // This is also doable
        // But not accessible in other lambdas.
        static std::vector<decltype(ws)> users2;
        // users2.push_back(ws);
        users.push_back(ws);
        ws->subscribe("sensors/ /house");
    },

    .close = [](auto *ws, int code, std::string_view message){
        users.erase(std::remove(users.begin(), users.end(), ws), users.end());

        // Not possible because not accessible.
        //users2.erase(std::remove(users2.begin(), users2.end(), ws), users2.end());
        std::cout << "Client disconnected!" << std::endl;
    },

    .message = [](auto *ws, std::string_view message, uWS::OpCode opCode){
        try{
            std::string message2 = std::string(message)   std::string(" ACK");
            for(const auto amp; ws2 : users)
                if(ws != ws2)
                    ws2->send(message2, opCode);

        }catch(std::exceptionamp; e){
            std::cout << e.what() << std::endl;
        }
    },
});
 

Теперь, нигде в main.cpp , нет необходимости передавать параметр в лямбда-функции. Вот откуда берется главная проблема.

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

1. Не уверен, что вы хотите для vec func(42) и func("hello") будет производить 2 разных vec2

2. Можем ли мы получить еще немного примеров того, что вы ищете? Начиная с C 17 с CTAD, вы можете использовать std::vector vec{ initializers }; , и тип элемента вектора будет выведен для вас.

3. Документ должен определить тип ws или тип .open (откуда вы знаете, что можете позвонить subscribe(const char*) ?).

4. На самом деле для этого проекта нет документа.

Ответ №1:

Проблема здесь в том, что auto не имеет типа, пока он не будет использован, auto сделайте его оператором вызова шаблона:

 #include <iostream>

using namespace std;

int main()
{
    auto func = [](auto param){
        std::cout << param << std::endl;
    };
   func(4);     // integer call
   func("lol"); //const char* call 

    return 0;
}
 

Следствием этого является то, что param может быть любой произвольный тип, прежде чем мы рассмотрим конкретное использование.

Если вы знаете аргумент, который будет предоставлен, то decltype его можно применить:

 #include <iostream>

using namespace std;

int main()
{
    int arg1 = 4;
    using Type1 = decltype(arg1); // type for the argument
    auto func = [](auto param){
        std::cout << param << std::endl;
    };
   func(arg1);  // Type1 call
   func("lol"); //const char* call 

    return 0;
}
 

Ответ №2:

Вы должны вручную просмотреть тип, который вы инициализируете.

Универсальный лямбда-код создает объект с шаблоном operator() . Там мало что мешает быть несколькими экземплярами этого шаблона.

Если это так, то не существует типа, который является типом auto parameter .

Вам относительно повезло здесь в том, что объект, который вы инициализируете всеми этими членами функтора, помещает аргументы прямо в его определение:

 MoveOnlyFunction<void(WebSocket<SSL, true, UserData> *)> open = nullptr;
 

Если вы не хотите снова просматривать определения, вы можете написать черту

 template<typename Signature>
struct MoveOnlyFunctionTraits;

template <typename R, typename... Arguments>
struct MoveOnlyFunctionTraits<MoveOnlyFunctionTraits<R(Arguments...)> {
    using result_type = R;

    static constexpr auto arity = sizeof...(Arguments);

    template <std::size_t I>
    using argument = std::tuple_element_t<std::tuple<Arguments...>, I>;
}

template<typename Signature, std::size_t I>
using argument_t = typename MoveOnlyFunctionTraits<Signature>::template argument<I>;
 

Это позволяет вам вытащить тип

 using websocket_type = argument_t<decltype(uWS::App::WebSocketBehaviour<DataType>::open), 0>;
std::vector<websocket_type> users;
 

Или вы можете отправить запрос на извлечение в библиотеку, которая предоставляет псевдонимы типов, которые вас интересуют

 template <bool SSL>
struct TemplatedApp {
    // existing members ...
public:
    template <typename DataType>
    using websocket_type = WebSocket<SSL, true, DataType>;
};
 

Затем

 std::vector<uWS::App::template websocket_type<DataType> *> users;
 

Ответ №3:

Или, может быть, вы могли бы использовать шаблоны.

 #include <vector>

template<typename type_t>
void some_function(const type_t value) 
{
    std::vector<type_t> vec;
    auto func = [](const type_tamp; parameter) 
    {
        std::vector<type_t> vec2;
    };

    //....
}

auto get_something()
{
    // some made up complex data type returned as auto
    using some_complex_data_type_from_somewhere_t = std::vector<std::vector<int>>;
    some_complex_data_type_from_somewhere_t value{};
    return value;
}

int main()
{
    auto value = get_something();
    some_function(value);

    return 0;
}
 

Ответ №4:

Вы можете «загрузить» вывод типа в псевдоним типа.

 void some_function() {
  using value_type = decltype(infer_from_somewhere);
  // alt: using value_type = typename some::annoyingly<complex>::type;

  std::vector<value_type> vec;
  auto func = [](value_type parameter){
    std::vector<value_type> vec2;
  };

  //...
}
 

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

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

2. использование не ограничивается глобальным охватом. Вы также можете сделать это локально внутри функции

3. @nmd_07 В вашем вопросе говорится, что причина, по которой используется auto, заключается в том, что типы являются сложными и длинными, а не в том, что они полностью неопределимы, вам, вероятно, следует уточнить, что,

4. @Frank, вы можете объявить тип параметра lambda, посмотрев исходный код для этой конкретной функции, но все документы платформы вместо этого используют auto, так как объявление для этого типа действительно длинное.