Marcus
Marcus

Reputation: 77

Illegal use of explicit template arguments while combating recursion errors

I am attempting to implement the SHA256 hash-function for use in-program with some compile-time abilities. A step in the pre-processing is padding the message/ input data given a formula. The amount of padding naturally has to be a positive integer. The algorithm relies on chunk processing and each chunk can only hold so much data, so if the data is larger than a chunk, we need more chunks. I solved this using recursion, but this throws C1202 - A template definition was recursive or exceeded complexity limits.

template <uint32_t DataLength, uint32_t ChunkSize, uint32_t Chunks>
int GetBitsToAppend() {
    static int BitsToAppend = ChunkSize*Chunks - DataLength - 1 - (ChunkSize / 8);
    return 0 > BitsToAppend ? GetBitsToAppend<DataLength, ChunkSize, Chunks+1>() : BitsToAppend;
}

I tried resolving it by adding an explicit template definition, but this instead throws a new error; C2768, Illegal use of explicit template arguments. Here is the code:

template<uint32_t DataLength, uint32_t ChunkSize>
int GetBitsToAppend<DataLength, ChunkSize, 1024>() {
    int BitsToAppend = ChunkSize*Chunks - DataLength - 1 (ChunkSize / 8)
}

It works as intended if I use a normal recurring function, however this breaks due to the necessity of having the length of the processing-array being known at compile time. e.g:

int GetBitsToAppend(uint32_t DataLength, uint32_t ChunkSize, uint32_t Chunks) {
    static int BitsToAppend = ChunkSize * Chunks - DataLength - 1 - (ChunkSize>>3);
    return 0 > BitsToAppend ? GetBitsToAppend(DataLength, ChunkSize, Chunks+1) : BitsToAppend;
}

template <typename T, size_t OutputSize, size_t ByteSizeInput, size_t Rounds, size_t ChunkSize>
std::bitset<OutputSize> SHA(const T data) {

    // Pre-processing
    const uint32_t DataLength = ByteSizeInput << 3;
    const uint32_t BitsToAppend = GetBitsToAppend(DataLength, ChunkSize, 1); // Run-time calculation

    const uint32_t TargetSize = (DataLength + BitsToAppend + (ChunkSize>>3) + 1) / sizeof(uint32_t);
    std::array<uint32_t, TargetSize> ProcessArray; // Invalid template argument.
    
    // Copy data into ProcessArray and perform remaining preprocessing
//........

ChunkSize is constant and given by the template parameters of the function, and so is the ByteSizeInput. ChunkSize>>3 refers to the size of the integer being appended in in the pre-processing, being 64-bits in SHA256/224, and 128-bits in SHA384/512. Aka. respective chunk sizes divided by eight.

Does anyone know how to fix this?

Upvotes: 0

Views: 153

Answers (2)

Marcus
Marcus

Reputation: 77

In addition to the answer provided kindly by mr. Sanders:

The compiler does not understand (in my experience) that the working template evaluates to constexpr when simply assigned to a variable, thus causing some further errors. Adding constepxr to the function signature of GetBitsToAppend as well as to BitsToAppend and the sum variable TargetSize provides the desired result:

template <uint32_t DataLength, uint32_t ChunkSize, uint32_t Chunks>
constexpr int GetBitsToAppend() {
    constexpr int BitsToAppend = ChunkSize * Chunks - DataLength - 1 - (ChunkSize/8);
    #pragma warning(suppress:4984) //Remove compatibility warning
    if constexpr (0 > BitsToAppend)
         return GetBitsToAppend<DataLength, ChunkSize, (Chunks+1)>();
    else
        return BitsToAppend;
}

template <typename T, size_t OutputSize, size_t ByteSizeInput, size_t Rounds, size_t ChunkSize>
std::bitset<OutputSize> SHA(const T data) {
    //Pre-processing
    constexpr uint32_t DataLength = ByteSizeInput << 3;
    constexpr uint32_t BitsToAppend = GetBitsToAppend<DataLength, ChunkSize, 1>();
    
    constexpr uint32_t TargetSize = (DataLength + BitsToAppend + (ChunkSize>>3)+1) / sizeof(uint32_t)
    std::array<uint32_t, TargetSize> ProcessArray; // Compiles!
//...

Upvotes: 1

catnip
catnip

Reputation: 25388

In C++17 and later, this can be fixed by using if constexpr, like this:

template <uint32_t DataLength, uint32_t ChunkSize, uint32_t Chunks>
int GetBitsToAppend() {
    constexpr int BitsToAppend = ChunkSize*Chunks - DataLength - 1 - (ChunkSize / 8);
    if constexpr (0 > BitsToAppend)
        return GetBitsToAppend<DataLength, ChunkSize, Chunks+1>();
    else
        return BitsToAppend;
}

Live demo

Upvotes: 0

Related Questions