How to implement conditional generic type dispatch in Rust

Like c++ did, I found it's a little hard to implement something like this in Rust
From: ClickHouse/NumberTraits.h at master · ClickHouse/ClickHouse (github.com)


template <bool is_signed, bool is_floating, size_t size>
struct Construct
{
    using Type = Error;
};

template <> struct Construct<false, false, 1> { using Type = UInt8; };
template <> struct Construct<false, false, 2> { using Type = UInt16; };
template <> struct Construct<false, false, 4> { using Type = UInt32; };
template <> struct Construct<false, false, 8> { using Type = UInt64; };
template <> struct Construct<false, false, 16> { using Type = UInt128; };
template <> struct Construct<false, false, 32> { using Type = UInt256; };
template <> struct Construct<false, true, 1> { using Type = Float32; };
template <> struct Construct<false, true, 2> { using Type = Float32; };
template <> struct Construct<false, true, 4> { using Type = Float32; };
template <> struct Construct<false, true, 8> { using Type = Float64; };
template <> struct Construct<true, false, 1> { using Type = Int8; };
template <> struct Construct<true, false, 2> { using Type = Int16; };
template <> struct Construct<true, false, 4> { using Type = Int32; };
template <> struct Construct<true, false, 8> { using Type = Int64; };
template <> struct Construct<true, false, 16> { using Type = Int128; };
template <> struct Construct<true, false, 32> { using Type = Int256; };
template <> struct Construct<true, true, 1> { using Type = Float32; };
template <> struct Construct<true, true, 2> { using Type = Float32; };
template <> struct Construct<true, true, 4> { using Type = Float32; };
template <> struct Construct<true, true, 8> { using Type = Float64; };

template <typename A, typename B> struct ResultOfModulo
{
    static constexpr bool result_is_signed = is_signed_v<A>;
    /// If modulo of division can yield negative number, we need larger type to accommodate it.
    /// Example: toInt32(-199) % toUInt8(200) will return -199 that does not fit in Int8, only in Int16.
    static constexpr size_t size_of_result = result_is_signed ? nextSize(sizeof(B)) : sizeof(B);
    using Type0 = typename Construct<result_is_signed, false, size_of_result>::Type;
    using Type = std::conditional_t<std::is_floating_point_v<A> || std::is_floating_point_v<B>, Float64, Type0>;
};

How to use generic to generate size_of_result and Type0 ?

I’m not familiar with C++ template programming; can you describe what you want to do in prose?


pub trait MyNumericType {
    type LargestType: MyNumericType;
    const SIGN: bool;
    const FLOATING: bool;
    const SIZE: usize;
}

struct ResultOfModulo<A, B>;

impl<A, B> IDataType for ResultOfModulo<A, B>
where
    A: MyNumericType,
    B: MyNumericType,
{
    // TODO implement is_sign, is_floating, size
    type Type = <Construct<A::SIGN, A::FLOATING, A::SIZE> as IDataType>::Type;
}

The error occurs: type provided when a constant was expected

1 Like

I found when I added

#![feature(const_evaluatable_checked)]

The compiler crashes, so I gave up using constant generics.

Const generics aren't really ready for this kind of logic yet. Does something like this work for you?

struct Signed;
struct Unsigned;

struct FloatingPoint;
struct Integer;

trait TypeConstructor { type Type; }

impl TypeConstructor for (Signed, Integer, [();4]) { type Type = i32; }

pub trait MyNumericType {
    type LargestType: MyNumericType;
    type Sign;
    type Repr;
    type Size;
}

struct ResultOfModulo<A, B>(std::marker::PhantomData<(A,B)>);

impl<A, B> TypeConstructor for ResultOfModulo<A, B>
where
    A: MyNumericType,
    B: MyNumericType,
    (A::Sign, A::Repr, A::Size): TypeConstructor
{
    // TODO implement is_sign, is_floating, size
    type Type = <(A::Sign, A::Repr, A::Size) as TypeConstructor>::Type;
}
2 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.