2013-03-04 4 views
11

C++11user-defined literals. Я только начал играть с ними, что заставило меня задаться вопросом, можно ли автоматически добавить все SI multipliers в один литерал, который я определяю?Как автоматически добавлять литеральные определения, основанные на одном определяемом пользователем литерале?

Например, если я определяю

Length operator "" _m(long double m) { 
    return Length(m); // Length in meters 
} 

где Length является подкласс некоторого Units базового класса, я хотел бы иметь механизм для автоматически оных (в том же духе, как boost operators) SI мультипликаторы для всех литералов, возвращающих Length:

// these are added automatically when defining the literal "_m": 
             // Length in: 
Length operator "" _Ym(long double Ym); // Yottameters 
Length operator "" _Zm(long double Zm); // Zetameters 
...          // ... 
...          // ... 
Length operator "" _km(long double km); // kilometers 
Length operator "" _mm(long double mm); // millimeters 
...          // ...  
...          // ... 
Length operator "" _zm(long double zm); // zeptometers 
Length operator "" _ym(long double ym); // yoctometers 

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

.. или я что-то пропускаю?

+0

Почему не с шаблонами? –

+0

@Adriano: пример? –

+0

То же, что и: http://www.codeproject.com/Articles/447922/Application-of-Cplusplus11-User-Defined-Literals-t (если вы можете опустить единицу измерения и сохранить множитель только ... I пропустил этот момент, вы правы, нам может понадобиться _macro magic_) –

ответ

3

Я не думаю, что есть способ сделать именно то, о чем вы просите, без «причудливых макросов». Это, насколько я мог бы получить:

template<typename T, T (*op)(long double)> 
struct SI 
{ 
    // ... 
    constexpr static T micro = op (.000001); 
    constexpr static T milli = op (.001); 
    constexpr static T kilo = op (1000); 
    constexpr static T mega = op (1000000); 
    // ... 
}; 

struct Length 
{ 
    constexpr Length(long double d) : _d(d) { } 
    constexpr operator long double() { return _d; } 
    long double _d; 
}; 

constexpr Length operator "" _m(long double m) { 
    return Length(m); 
} 

typedef SI<Length, ::operator "" _m> SI_Length; 

int main() 
{ 
    constexpr Length l = 3 * SI_Length::kilo; 
    static_assert(l == 3000, "error"); 
} 

Если странные макросы разрешены, то что-то вроде следующего должен делать эту работу:

#define DEFINE_SI_MULTIPLIERS(T, unit) \ 
    constexpr T operator "" _u ## unit(long double m) \ 
    { return ::operator "" _ ## unit(0.000001 * m); } \ 
    constexpr T operator "" _m ## unit(long double m) \ 
    { return ::operator "" _ ## unit(0.001 * m); } \ 
    constexpr T operator "" _k ## unit(long double m) \ 
    { return ::operator "" _ ## unit(1000 * m); } \ 
    // ... 

DEFINE_SI_MULTIPLIERS(Length, m) 

int main() 
{ 
    constexpr Length l = 3.0_km; 
    static_assert(l == 3000, "error"); 
} 
+1

hmm ... nice try (+1), но это не сильно отличается от простого умножения на 'const' SI-коэффициенты, определенные в пространстве имен (например,' Length L = 3 * si :: kilo; '); ИМХО это не добавляет много выразительной силы ... –

+0

@RodyOldenhuis: True. Как я уже говорил, это насколько я могу получить, и это, вероятно, не очень далеко. Я считаю, что макросы - это единственный способ добиться того, что вы ищете. –

+0

Я тоже так считаю. Принято! –

-1

Не можете ли вы использовать аромат operator "" _m(const char *), если вы готовы самостоятельно разбирать поплавки? Это позволяет писать 1234k_m, вызывая общий синтаксический анализатор SI для ваших значений с плавающей запятой.

+2

Считаете ли вы, что это должно работать? Используя gcc 4.7.2, это анализируется как '1234'' k_m', а gcc ищет 'operator '" k_m', см. [Liveworkspace] (http://liveworkspace.org/code/1YjxLZ$0). –