Необязательные и переменно упорядоченные аргументы функций в C ++ 17 - PullRequest
2 голосов
/ 24 октября 2019

Я пытаюсь улучшить конструктор класса последовательного порта. В настоящее время существует множество перегрузок для обработки различных сценариев (порт, скорость передачи данных, биты данных, четность, открытие при создании, обратные вызовы и т. Д.). Чтобы позволить пользователю этого класса передавать только те параметры, которые ему нужны в произвольном порядке, я начал следующим образом:

#include <iostream>
#include <string>
#include <tuple>

template <typename T, typename Tuple>
struct has_type;

template <typename T, typename... Us>
struct has_type<T, std::tuple<Us...>> : std::disjunction<std::is_same<T, Us>...> {};

template <typename ...UnorderedArgs>
std::string getPort(std::tuple<UnorderedArgs...> arg_set)
{
    if constexpr (has_type<std::string, std::tuple<UnorderedArgs...>>::value)
        return std::get<std::string &&>(std::move(arg_set));
    else
        return "NotSet";
}

class SerialPort
{
public:
    template <typename... Args>
    SerialPort(Args ...args) :
        SerialPort(std::forward_as_tuple(std::move(args)...))
    {}

    template <typename ...UnorderedArgs>
    SerialPort(std::tuple<UnorderedArgs...> arg_set) :
        SerialPort(getPort(std::move(arg_set)))
    {}

    SerialPort(const std::string &port) // [X]
    {
        std::cout << "SerialPort " << port << std::endl;
    }
};

int main()
{
    std::string port = "/dev/tty";
    SerialPort sp(1, port); // without 1 the compiler would use [X]
    return 0;
}

Этот код устанавливает порт на NotSet, поэтому часть с if constexpr не работает, как задумано. Как это можно исправить?

Ответы [ 2 ]

2 голосов
/ 24 октября 2019

Этот код устанавливает порт на NotSet, поэтому деталь с if constexpr не работает должным образом. Как это можно исправить?

Это проблема, если ссылки: когда вы проверяете наличие std::string в has_type, std::string присутствует, но со ссылкой (&&, заданнойstd::move(), если я не ошибаюсь).

Решение: удалить ссылки

template <typename T, typename... Us>
struct has_type<T, std::tuple<Us...>>
  : std::disjunction<std::is_same<T, std::remove_reference_t<Us>>...> {};
// ..................................^^^^^^^^^^^^^^^^^^^^^^^^^^^

- РЕДАКТИРОВАТЬ -

Как указаноJarod42 (спасибо!), таким образом, имя has_type больше не является правильным.

А может быть, вам нужен has_type, который обнаруживает наличие проверки типа и ссылок ...

Может быть, лучше сохранить оригинальный has_type и использовать std::remove_reference_t, вызывая его

// ............................................VVVVVVVVVVVVVVVVVVVVVVV
if constexpr (has_type<std::string, std::tuple<std::remove_reference_t<UnorderedArgs>...>>::value)
    return std::get<std::string &&>(std::move(arg_set));
else
    return "NotSet";
1 голос
/ 24 октября 2019

Почему вы ограничиваете себя тем, что C ++ предлагает синтаксически? Эта проблема требует изменения шаблона построения и свободного API.

Используйте что-то вроде этого:

class serial_port_options
{
    /* private fields to hold options */
public:
    serial_port_options& set_port( int );
    serial_port_options& set_baudrate( int ); 
    serial_port_options& set_data_bits( int );
    serial_port_options& set_parity( bool );
    serial_port_options& set_open_on_creation( bool ); 
    serial_port_options& set_callbacks( CALLBACK );

    int get_port() const;
    int get_baudrate() const;
    int get_data_bits() const;
    bool get_parity() const;
    bool get_open_on_creation() const;
    CALLBACK get_callbacks() const;
};

class SerialPort
{
public:
     SerialPort(const serial_port_options&);

/* other logic and public interface */
};

Используя вышеизложенное, вы можете построить свой объект параметров:

serial_port_options options;
options.set_port(80).set_baudrate(65536).set_data_bits(7)
       .set_parity(false).set_open_on_creation(true);

SerialPort port(options);
...