Найти тип для шаблона по какому-то идентификатору или int - PullRequest
2 голосов
/ 08 мая 2019

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

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

Test::get<ID2>()

Что работает в приведенном ниже коде, включая тип. Есть ли простой / умный способ избежать написания типа в строке get?

Я пытался использовать typedef для идентификации, конечно, покупка, которая напрямую разрешается для примитивного типа, и две специализации шаблона неоднозначны. Если нет хорошего C ++ способа, я все еще могу использовать генерацию кода для решения этой проблемы, но я надеюсь на лучшее решение.

#include <iostream>

typedef int ID1_t;
typedef double ID2_t;

enum  {
    ID1,
    ID2,
};

class Test
{
public:
    template<int I, typename T> static T get();
};

template<> int Test::get<ID1>()
{
  return 43;   
}

template<> double Test::get<ID2>()
{
  return 0.12;   
}

int main()
{
  std::cout << Test::get<ID2, ID2_t>() << std::endl;
  std::cout << Test::get<ID1, ID1_t>() << std::endl;
}

РЕДАКТИРОВАТЬ (немного больше фона): Я пишу центральный, чтобы использовать компонент для получения данных (например, int) и установить данные с помощью механизма уведомлений, чтобы проинформировать все необходимые места. Его централизованно определенная, но децентрализованная реализация заключается в том, что интерфейс одинаков для всех целей, но реализация может быть в собственном исходном файле. Это значительно уменьшает зависимости между компонентами, но также обеспечивает обмен типизированными данными.

Например:

Допустим, у нас есть данные, записанные во флэш-память во время производства, с данными конфигурации, хранящимися в EEPROM или FRAM, а также некоторые изменчивые данные в переменных RAM, которые будут забыты при цикле питания.

Давайте возьмем значение громкости для динамика, которое можно как-то изменить. Я просто хочу поместить значение в систему:

auto volume = 55; //(constant here but is in real decoded from SPI)
CentralDataStorage::set<Volume>(volume);

Я не хочу делать здесь зависимость от физического хранилища. Чем я хочу компонент, который отвечает за хранение значения, а также возвращает значение по запросу. Таким образом, он реализует метод set и get, но только для идентификатора Volume .

Другой компонент, который фактически устанавливает громкость в усилителе, получает уведомление и использует метод get из центрального интерфейса, который реализован где-то, чего этот компонент не знает.

auto volume = CentralDataStorage::get<Volume>();

Все известно во время компиляции и должно быть безопасным для типа.

Ответы [ 4 ]

2 голосов
/ 09 мая 2019

Я нашел способ, используя части других ответов и покажу реализацию здесь:

Центральный заголовочный файл для определения всех идентификаторов и типов. decltype помогает найти тип возвращаемого значения.Использование auto приводит меня к проблеме, которая, когда я объявляю здесь метод для определенного типа, не будет соответствовать объявлению шаблона

//Test.hpp
struct ID1 {
    int value; 
};

struct ID2 {
    double value; 
};

struct Test {
    template<typename T> static decltype(T::value) get();
};

Тогда у меня есть определение функции get(который также может быть в отдельных единицах компиляции)

#include "Test.hpp"
template<> int Test::get<ID1>()
{
    return 43;
}

template<> double Test::get<ID2>()
{
    return 0.12;
}

и основной, который их использует

#include "Test.hpp"
int main()
{
  std::cout << Test::get<ID2>() << std::endl;
  std::cout << Test::get<ID1>() << std::endl;
}

Это в значительной степени взяло ответы Джорджа Кортиса и Гийома Рашико вместе и сложилоеще немного во мне.

2 голосов
/ 08 мая 2019

Сначала я объясню свой предпочтительный способ делать такие вещи.Я обычно стараюсь избегать enum, так как почти каждый раз, когда вам нужны дополнительные данные, а с enum вы застряли, записывая эти данные в другом месте.Например, это может быть в специализации шаблона.

Я бы предпочел использовать типы:

struct ID1 {
    static constexpr auto default_value = 42; 
};

struct ID2 {
    static constexpr auto default_value = 0.12; 
};

Затем отбросить специализацию и использовать данные, предоставленные в типах:

struct Test {
    template<typename T>
    static auto get() {
        // or maybe call a static member function
        return T::default_value;
    }
};

int main()
{
  std::cout << Test::get<ID2>() << std::endl;
  std::cout << Test::get<ID1>() << std::endl;
}

Но есть способ заставить ваш проект работать и избежать написания этого типа, используя вывод типа возврата:

#include <iostream>

enum {
    ID1,
    ID2,
};

struct Test {
    // to be deduced -------v
    template<auto I> static auto get();
    //       ^--- (optional change)
    //            Have the enum type deduce as well
};

template<> auto Test::get<ID1>()
{
  return 43;   
}

template<> auto Test::get<ID2>()
{
  return 0.12;   
}

int main()
{
  std::cout << Test::get<ID2>() << std::endl;
  std::cout << Test::get<ID1>() << std::endl;
}

См. Компиляцию в проводнике компилятора .

Вы также можете вернуться к int, поскольку параметр шаблона enum остается совместимым с C ++ 14:

template<int I> static auto get();
//       ^------ reverted it back to int

Но, используя C ++ 17 и параметры автоматического шаблона, вы можетеиметь множественное перечисление без конфликта значений:

enum struct A {
    Value1, Value2
};

enum struct B {
    Value1
};

// Different with C++17 auto template parameter
// not possible with C++14
template<> auto Test::get<A::Value1>()
{
  return 43;   
}

template<> auto Test::get<B::Value1>()
{
  return 0.12;   
}
1 голос
/ 08 мая 2019

Полагаю, вы хотели этого?

#include <iostream>

typedef int ID1;
typedef double ID2;

class Test
{
public:
    template<typename T> static T get();
};

template<> int Test::get<ID1>()
{
  return 43;   
}

template<> double Test::get<ID2>()
{
  return 0.12;   
}

int main()
{
  std::cout << Test::get<ID2>() << std::endl;
  std::cout << Test::get<ID1>() << std::endl;
}

Идея состоит в том, что если тип данных известен во время компиляции, то нет необходимости записывать число для выбора типа данных.Тип данных сам по себе достаточен для выполнения работы.

0 голосов
/ 08 мая 2019

Вы имеете в виду, разделить объявление на две части?

template<int i> struct id_type;
template<int i> using id_type_t = typename id_type<i>::type;
template<> struct id_type<ID1> { using type = int; }

template<int i> id_type_t<i> get();
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...