Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Any way to make parameterized user defined literals?

A little while ago I had an idea about "parameterized" user-defined literals and was wondering if there is any way to do this in the current C++ standard.

Basically, the idea is to have a user-defined literal whose behaviour can be tweaked according to some parameters. As a simple example, I chose a "fixed-point" literal which turns a floating-point number into an integer; the parameter is the precision in terms of the number of decimal places.

This is just an exercise for now, since I'm not sure how or if this would be useful in a real application.

My first idea went something like this:

namespace fp_impl {
    constexpr int floor(long double n) {
        return n;
    }

    constexpr int pow10(int exp) {
        return exp == 0 ? 1 : 10 * pow10(exp - 1);
    }

    template<int i>
    constexpr int fixed_point(long double n) {
        return floor(n * pow10(i));
    }

    namespace fp2 {
        constexpr int operator"" _fp (long double n) {
            return fixed_point<2>(n);
        }
    }

    namespace fp4 {
        constexpr int operator"" _fp (long double n) {
            return fixed_point<4>(n);
        }
    }
}

template<int prec> struct fp;
template<> struct fp<2> {
    namespace lit = fp2;
};
template<> struct fp<4> {
    namespace lit = fp4;
};

int main() {
    {
        using namespace fp<2>::lit;
        std::cout << 5.421_fp << std::endl; // should output 542
    }
    {
        using namespace fp<4>::lit;
        std::cout << 5.421_fp << std::endl; // should output 54210
    }
}

However, it doesn't compile because namespace aliases are not permitted at class scope. (It also has a problem with requiring you t manually define every version of operator"" _fp.) So I decided to try something with macros:

namespace fp {
    namespace detail {
        constexpr int floor(long double n) {
            return n;
        }

        constexpr int pow10(int exp) {
            return exp == 0 ? 1 : 10 * pow10(exp - 1);
        }

        template<int i>
        constexpr int fixed_point(long double n) {
            return floor(n * pow10(i));
        }
    }
}

#define SPEC(i) \
    namespace fp { \
        namespace precision##i { \
            constexpr int operator"" _fp(long double n) { \
                return fp::detail::fixed_point<i>(n); \
            } \
        } \
    }
SPEC(2); SPEC(4);
#undef SPEC
#define fp_precision(i) namespace fp::precision##i

int main() {
    {
        using fp_precision(2);
        std::cout << 5.421_fp << std::endl;
    }
    {
        using fp_precision(4);
        std::cout << 5.421_fp << std::endl;
    }
}

This works, though it still has the requirement of using the SPEC() macro for every precision you ever want to use. Of course, some preprocessor trickery could be used to do this for every value from, say, 0 to 100, but I'm wondering if there could be anything more like a template solution, where each one is instantiated as it is needed. I had a vague idea of using an operator"" declared as a friend function in a template class, though I suspect that won't work either.

As a note, I did try template<int i> constexpr int operator"" _fp(long double n), but it seems this is not an allowed declaration of a literal operator.

like image 914
celticminstrel Avatar asked Nov 14 '25 14:11

celticminstrel


2 Answers

You can return a class type that has operator()(int) overloaded from your literal operator. Then you could write

5.421_fp(2);
like image 56
Johannes Schaub - litb Avatar answered Nov 17 '25 07:11

Johannes Schaub - litb


A user-defined literal function takes as its sole argument the literal itself. You can use state outside the function, for example with a global or thread-local variable, but that's not very clean.

If the argument will always be compile-time constant, and it's part of the number, pass it through the literal itself. That requires writing an operator "" _ ( char const *, std::size_t ) overload or template< char ... > operator "" _ () template and parsing the number completely by yourself.

You will have to work such a parameter into the existing floating-point grammar, though. Although C++ defines a very open-ended preprocessing-number construct, a user-defined literal must be formed from a valid token with a ud-suffix identifier appended.

You might consider using strings instead of numbers, but then the template option goes away.

like image 31
Potatoswatter Avatar answered Nov 17 '25 08:11

Potatoswatter



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!