简体   繁体   中英

Replacing a functor by a templated function

I'm currently working on refreshing an old C++ project of mine, trying to take advantage of all the good stuff brought by modern C++. Some part of the project was about accessing an array of uint8_t, returning a uint8_t, a uint16_t or a uint32_t, depending on the data needed. For the simplicity of the question, I'll leave any endianness related problems aside.

I did the following class, using a functor to access the data :

template<std::size_t sz>
class Read
{
public:
    template <std::size_t sz>
    struct Read_traits { };

    template <>
    struct Read_traits<8> { using internal_type = std::uint8_t; };

    template <>
    struct Read_traits<16> { using internal_type = std::uint16_t; };

    template <>
    struct Read_traits<32> { using internal_type = std::uint32_t; };

    template <>
    struct Read_traits<64> { using internal_type = std::uint64_t; };

    using read_type = typename Read_traits<sz>::internal_type;

    template<typename T, ::std::size_t N>
    read_type operator()(const ::std::array<T, N> & arr, const ::std::uint32_t& adr) const {
        read_type returnValue{};
        for (uint8_t i = 0; i <= sizeof(read_type) - 1; ++i) {
            returnValue <<= 8;
            returnValue |= arr[adr + i];
        }
        return returnValue;
    };
};

Usage is

int main()
{
    std::array <uint8_t, 0x4> memory {0xFE, 0xDC, 0xBA, 0x98};
    Read<32> r32;
    auto val32 = r32(memory, 0);
    std::cout << "32 bits: 0x" << std::hex << val32 << std::endl;

    return 0;
}    

It works the way I want, but I was wondering if it was possible to create a regular function in place of the functor, still using the traits ?

IE replacing the 2 lines functor call :

 Read<32> r; auto val = r(memory, 0); 

by a single line function :

 auto val Read<32>(memory, 0); 

I tried a few things that weren't conclusive, and as I'm far from being an expert in templates, I might be chasing after something that's not even doable ...

Thanks for reading me :)

You could do this:

// Maps 8, 16 and 32 to uint8_t, uint16_t and uint32_t, respectively
template<size_t Size>
using SizedUInt = std::conditional_t<Size == 8, uint8_t,
                   std::conditional_t<Size == 16, uint16_t,
                    std::conditional_t<Size == 32, uint32_t, void>>>;

template<size_t Size, typename T, size_t N>
SizedUInt<Size> read(const std::array<T, N>& arr, uint32_t adr)
{
    SizedUInt<Size> returnValue{};
    for (uint8_t i = 0; i <= sizeof(SizedUInt<Size>) - 1; ++i) {
        returnValue <<= 8;
        returnValue |= arr[adr + i];
    }
    return returnValue;
}

SizedUInt is a template alias which selects correct type using nested conditional_t template. I've set the "false" type of the deepest conditional_t to void in order to trigger compilation error, when the template is used with value different than 8, 16 or 32.


Or, you could do this, using your template class:

// create a temporary of type Read<32> and call its call operator
auto val = Read<32>{}(memory, 0);

The only reason for Read as a class seems to be the definition of Read_traits . But you can define the latter outside of the former too. Just a few additional typename keywords necessary due to C++ uglyness...

Note that the sz template parameter is provided first. When invoking, it is the only parameter used explicitly. The rest is deducted, same as before.

#include <cstdint>
#include <iostream>
#include <array>

template <std::size_t sz>
struct ReadType { };

template <>
struct ReadType <8> { using type = std::uint8_t; };

template <>
struct ReadType <16> { using type = std::uint16_t; };

template <>
struct ReadType <32> { using type = std::uint32_t; };

template <>
struct ReadType <64> { using type = std::uint64_t; };

template <std::size_t sz, typename T, ::std::size_t N>
typename ReadType<sz>::type read(const ::std::array<T, N> & arr, const ::std::uint32_t& adr) {
    using read_type = typename ReadType<sz>::type;
    read_type returnValue{};
    for (uint8_t i = 0; i <= sizeof(read_type) - 1; ++i) {
        returnValue <<= 8;
        returnValue |= arr[adr + i];
    }
    return returnValue;
};

int main()
{
    std::array <uint8_t, 0x4> memory {0xFE, 0xDC, 0xBA, 0x98};
    auto val32 = read<32>(memory, 0);
    std::cout << "32 bits: 0x" << std::hex << val32 << std::endl;

    return 0;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM