Search code examples
c++fixed-length-record

What is the most simple/elegant way to calculate the length of a number written as text?


Given the maximum possible value, how to simply express the space needed to write such number in decimal form as text ?

The real task: logging process ids (pid_t) with fixed length, using gcc on Linux. It'd be good to have a compile time expression to be used in the std::setw() iomanipulator.

I have found that linux/threads.h header contains a PID_MAX value with the maximum pid allocated to a process. So having

#define LENGTH(t) sizeof(#t)-1

the LENGTH(PID_MAX) would be a compile time expression, but unfortunatelly this number is defined in hexa:

#define PID_MAX 0x8000

My current best solution is a bit oddish

static_cast<int>( ::floor( ::log(PID_MAX)/::log(10) + 1 ) );

But this is calculated runtime and uses functions from math.h


Solution

  • You could do it with a little template meta programming:

    //NunLength_interal does the actual calculation. 
    template <unsigned num>
    struct NumLength_internal
    { enum { value = 1 + NumLength_internal<num/10>::value }; };
    
    template <>
    struct NumLength_internal<0>
    { enum { value = 0 }; };
    
    //NumLength is a wrapper to handle zero. For zero we want to return
    //a length of one as a special case.
    template <unsigned num>
    struct NumLength
    { enum { value = NumLength_internal<num>::value };};
    
    template <>
    struct NumLength<0>
    { enum { value = 1 }; };
    

    This should work for anything now. For example:

    cout << NumLength<0>::value      << endl; // writes: 1
    cout << NumLength<5>::value      << endl; // writes: 1
    cout << NumLength<10>::value     << endl; // writes: 2
    cout << NumLength<123>::value    << endl; // writes: 3
    cout << NumLength<0x8000>::value << endl; // writes: 5
    

    This is all handled at compile time.

    Edit: I added another layer to handle the case when the number passed in is zero.