Search code examples
c++boosttype-traitsboost-multiprecision

Type trait for boost multiprecision integers


I want to write a type trait to identify boost multiprecision integer types. I can do this for a concrete type like uint256_t:

template <typename T>
struct is_multiprecision_int : std::false_type {};

template <>
struct is_multiprecision_int<uint256_t> : std::true_type {};

But how can I do the same for ANY boost multiprecision integer (or at least for any multiprecision number with a cpp_int backend)?


Solution

  • Okay, I'll take a crack at this.

    Based on the official documentation for boost-multiprecision: https://www.boost.org/doc/libs/1_73_0/libs/multiprecision/doc/html/boost_multiprecision/tut/ints/cpp_int.html, this may be a possible option:

    #include<type_traits>
    
    using namespace boost::multiprecision;
    
    //helper struct for checking if type is cpp_int_backend at compile time using function overloading
    template<typename T>
    struct is_cpp_int_backend{
        //this template will trigger for pointer to any 
        //  specialisation of cpp_int_backend. Pass by pointer instead
        //  of value, for more generic usage (passing by value will
        //  only work if cpp_int_backend has move constructor defined,
        //  whereas pass-by-pointer will work regardless). References
        //  are also acceptable, however, using pointers will result in
        //  compile error if value is passed, whereas references may give
        //  unexpected behaviour. For these reasons, prefer pointers. 
        template<uint A, uint B, cpp_integer_type CIT, cpp_int_check_type C, typename TT>
        constexpr static std::true_type test(cpp_int_backend<A,B,CIT,C,TT>*);
    
        //this overload has the lowest precedence during overload
        //  resolution, but will accept any argument.  
        constexpr static std::false_type test(...);
    
        //type will be std::true_type or std::false_type depending
        //  on which overload is selected. If T is a specialisation
        //  of cpp_int_backend, it will be true_type, else false_type
        using type = decltype(test(std::declval<T*>())); 
        constexpr static bool value = type::value;
    };
    
    //use similar technique to above, to create type trait for 
    //  multiprecision type
    template<typename T>
    struct is_multiprecision{
    
        //enable this template, if T is a specialisation of 'number'
        //  in boost::multiprecision, and the nested template parameter
        //  is a cpp_int_backend. Use pointers for similar reason as 
        //  above
        template<typename TT, typename = std::enable_if_t<is_cpp_int_backend<TT>::value>>
        constexpr static std::true_type test(number<TT>*);
    
        //again, lowest order of precedence, but will trigger for
        //  anything the above function template does not trigger for
        constexpr static std::false_type test(...);
    
        //get type depending on T
        using type = decltype(test(std::declval<T*>()));
        constexpr static bool value = type::value; 
    };
    
    //variable template for convenience
    template<typename T>
    constexpr bool is_multiprecision_v = is_multiprecision<T>::value;
    
    //example usage
    static_assert(is_multiprecision_v<uint256_t>);
    

    It works on my machine.