Дифференцировать typedefs - PullRequest
       23

Дифференцировать typedefs

5 голосов
/ 19 апреля 2019

Я пишу абстракцию C ++ для библиотеки C.В библиотеке C есть несколько определений типов для идентификаторов, которые идентифицируют удаленные ресурсы:

typedef int color_id;
typedef int smell_id;
typedef int flavor_id;
// ...

color_id createColor( connection* );
void destroyColor( connection*, color_id );
// ...

Все эти определения типов, разумеется, имеют одинаковый тип в глазах компилятора.Это проблема для меня, потому что я хотел бы перегружать функции и специализировать шаблоны, чтобы предлагать приятный C ++ дружественный API:

// can't do the following since `color_id`, `smell_id` and `int` are the same
std::ostream& operator<<( std::ostream&, color_id );
std::ostream& operator<<( std::ostream&, smell_id );
void destroy( connection*, color_id );
void destroy( connection*, smell_id );

// no static check can prevent the following
smell_id smell = createSmell( connection );
destroyColor( connection, smell ); // it's a smell, not a color!
  • Как я могу дифференцировать эти идентификаторы, чтобы воспользоваться преимуществами безопасности типови перегружать / специализировать функции и классы?

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

  1. Существует много кода, уже специализированного для примитивных типов (например, std::hash).
    Isесть ли способ сказать компилятору что-то вроде «если что-то имеет специализацию для int, но не для моей оболочки, то просто используйте специализацию int»?
    Должен ли я написать специализацию для таких вещей, как std::hash?А как насчет похожих шаблонных структур, которых нет в std (например, вещи в boost, Qt и т. Д.)?

  2. Должен ли я идти с неявным или явным конструктором и оператором приведения?Явные, конечно, безопаснее, но из-за них было бы очень утомительно взаимодействовать с существующим кодом и сторонними библиотеками, использующими C API.

Я более чем открыт для любых советовот того, кто там уже был!

Ответы [ 2 ]

3 голосов
/ 19 апреля 2019

Один класс-обертка, чтобы управлять ими всеми.

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

template<class ID> 
struct ID_wrapper
{
    constexpr static auto name() -> decltype(ID::name()) {
        return ID::name(); 
    }
    int value;

    // Implicitly convertible to `int`, for C operability
    operator int() const {
        return value; 
    }  
};

Перегрузка std::hash (только один раз)

Мы можем придерживаться любых черт, которые мы хотим в классе ID, но я предоставил name() какпример.Поскольку ID_Wrapper написан как шаблон, специализировать его для std::hash и других классов нужно только один раз:

template<class ID>
class std::hash<ID_wrapper<ID>> : public std::hash<int>
{
   public:
    // I prefer using Base to typing out the actual base
    using Base = std::hash<int>;

    // Provide argument_type and result_type
    using argument_type = int;
    using result_type = std::size_t; 

    // Use the base class's constructor and function call operator
    using Base::Base; 
    using Base::operator(); 
};

Распечатать идентификатор с его именем

Если выхочу, мы могли бы также специализировать operator<<, но ID_wrapper неявно конвертируется в int в любом случае:

template<class ID>
std::ostream& operator<<(std::ostream& stream, ID_Wrapper<ID> id) {
    stream << '(' << ID_Wrapper<ID>::name() << ": " << id.value << ')'; 
    return stream; 
}

Получив это, мы просто напишем класс признаков для каждого типа идентификатора!

struct ColorIDTraits {
    constexpr static const char* name() {
        return "color_id";
    }
};

struct SmellIDTraits {
    constexpr static const char* name() {
        return "smell_id";
    }
};

struct FlavorIDTraits {
    constexpr static const char* name() {
        return "flavor_id";
    }
};

Оборачивая все это вместе

Тогда мы можем typedef упаковщик ID:

using color_id = ID_wrapper<ColorIDTraits>;
using smell_id = ID_wrapper<SmellIDTraits>;
using flavor_id = ID_wrapper<FlavorIDTraits>;
0 голосов
/ 30 апреля 2019

Использовать BOOST_STRONG_TYPEDEF , как прокомментировал @ MooingDuck.

...