Search code examples
c++c++11operator-overloadinguser-defined-literals

How to automatically add literal definitions, based on a single user-defined literal?


C++11 offers user-defined literals. I've just started to play around with them, which made me wonder whether it would be possible to automatically add all SI multipliers to a single literal I define?

For example, if I define

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

where Length is a subclass of some Units base class, I would like to have a mechanism to automatically add (in the same spirit as boost operators) SI multipliers for all literals that return a 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

As far as I could see, aside from perhaps some macro magic, there is no way to do this automatically since all user-defined literals need an explicit definition.

..or am I overlooking something?


Solution

  • I do not think there is a way to do exactly what you are asking for without "bizarre macros". This is as far as I could get:

    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");
    }
    

    If bizarre macros are allowed, then something like the following should do the job:

    #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");
    }