@Vadimatorikda
Инженер-программист, embedded разработчик

Выбор типа переменной класса в зависимости от параметра шаблона (C++17, if constexpr)?

Имеется шаблон класса. Он принимает параметр enum class. Требуется в зависимости от выбранного параметра создать переменную заданного размера. Пример того, как я себе это представлял:
template < EC_SPI_CFG_DATA_FRAME FRAMES >
class spi {
    constexpr spi() {};
    if constexpr ( FRAMES == EC_SPI_CFG_DATA_FRAME::FRAME_8_BIT ) {
        mutable uint8_t* p_tx = nullptr;
    } else {
        mutable uint16_t* p_tx = nullptr;
    }
};

Однако получаю следующее:
spi.h:137: ошибка: expected unqualified-id before 'if'
     if constexpr ( FRAMES == EC_SPI_CFG_DATA_FRAME::FRAME_8_BIT ) {
     ^~
spi.h:139: ошибка: expected unqualified-id before 'else'
     } else {
       ^~~~

В параметрах компиляции C++ файла стоит флаг -std=c++1z.
  • Вопрос задан
  • 618 просмотров
Решения вопроса 1
@MarkusD Куратор тега C++
все время мелю чепуху :)
Ты можешь добиться желаемого используя частную специализацию, например.

Суть такова. Нужно превратить значения типа перечисления в тип. Сделать мы это можем используя возможность указывать в качестве параметра шаблона значения перечислимых типов.

Например:
enum class AllowedTypes : uint8_t
{
	Int8	= 0,
	Uint8,
	Int16,
	Uint16,
};

template< AllowedTypes TYPE >
struct AllowedType;


А для того, чтобы конкретное значение перечисления правильно переводилось в тип, шаблон "AllowedType" нужно специализировать.
spoiler
template<>
struct AllowedType<AllowedTypes::Int8>
{
	using Type = int8_t;
};

template<>
struct AllowedType<AllowedTypes::Uint8>
{
	using Type = uint8_t;
};

template<>
struct AllowedType<AllowedTypes::Int16>
{
	using Type = int16_t;
};

template<>
struct AllowedType<AllowedTypes::Uint16>
{
	using Type = uint16_t;
};


После этого достаточно использовать "typename AllowedType::Type" в нужном тебе месте.
spoiler
template< AllowedTypes TYPE >
struct spi
{
	typename AllowedType<TYPE>::Type*	p_tx = nullptr;
	
	constexpr spi() {};
};


Вот пример работы:
spoiler
// Example program
#include <typeinfo>
#include <iostream>
#include <string>
#include <cstdint>

enum class AllowedTypes : uint8_t
{
	Int8	= 0,
	Uint8,
	Int16,
	Uint16,
};

template< AllowedTypes TYPE >
struct AllowedType;

template<>
struct AllowedType<AllowedTypes::Int8>
{
	using Type = int8_t;
};

template<>
struct AllowedType<AllowedTypes::Uint8>
{
	using Type = uint8_t;
};

template<>
struct AllowedType<AllowedTypes::Int16>
{
	using Type = int16_t;
};

template<>
struct AllowedType<AllowedTypes::Uint16>
{
	using Type = uint16_t;
};

template< AllowedTypes TYPE >
using TypeFor = typename AllowedType<TYPE>::Type;


template< AllowedTypes TYPE >
struct spi
{
	TypeFor<TYPE>*	p_tx = nullptr;
	
	constexpr spi() {};
};

int main()
{
	spi<AllowedTypes::Int8> spi1;
	std::cout << typeid( *spi1.p_tx ).name() << " " << sizeof( *spi1.p_tx ) << std::endl;
	
	spi<AllowedTypes::Uint16> spi2;
	std::cout << typeid( *spi2.p_tx ).name() << " " << sizeof( *spi2.p_tx ) << std::endl;
	
	return 0;
}

cpp.sh/85p6
Ответ написан
Пригласить эксперта
Ответы на вопрос 2
Cyapa
@Cyapa
Лучше воспользоваться частной специализацией и сделать вот так:

template < EC_SPI_CFG_DATA_FRAME FRAMES >
struct spi
{
    mutable uint16_t* p_tx = nullptr;
};

template < >
struct spi<EC_SPI_CFG_DATA_FRAME::FRAME_8_BIT>
{
    mutable uint8_t* p_tx = nullptr;
};
Ответ написан
devalone
@devalone
̻̻̻̻̻̻̻̻̻̻̻̻̻̻̻̻̻̻̻̻̻̻̻̻̻̻̻̻̻̻̻̻̻̻̻̻̻̻̻̻̻̻̻̻̻̻̻̻̻̻
Возможно вам нужно что-то подобное:
#include <iostream>
#include <type_traits>

template <typename U>
struct Data {
    int value;
};
template <>
struct Data<double> {
    double value;
};

template <typename T>
class Test {

    Data<T> data;

public:
    void test()
    {
        if (typeid(data.value).hash_code() == typeid(int).hash_code())
            std::cout << "int";
        else if (typeid(data.value).hash_code() == typeid(double).hash_code())
            std::cout << "double";
        else
            std::cout << "other";

        std::cout << std::endl;
    }
};

int main(int argc, char* argv[])
{
    Test<int> t1;
    Test<double> t2;
    Test<char> t3;

    t1.test();
    t2.test();
    t3.test();

    return 0;
}

data.value во всех случаях int за исключением того, когда T double. Решение конечно не самое красивое и магистры шаблонной магии возможно побьют меня за него, но работает :)
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы