TL;DR
A couple of templatized and overloaded non-templatized member functions in a non-templatized class should all end up routing through the same member function to perform the actual work. All the overloads and templatizations are done to convert the "data buffer" into gsl::span<std::byte>
type (essentially a close relative to std::array<std::byte, N>
from the Guidelines Support Library)
Wall of code
#include <array>
#include <cstdlib>
#include <iostream>
#pragma warning(push)
#pragma warning(disable: 4996)
#include <gsl.h>
#pragma warning(pop)
// convert PoD into "memory buffer" for physical I/O
// ignore endianness and padding/alignments for this example
template<class T> gsl::span<std::byte> byte_span(T& _x) {
return { reinterpret_cast<std::byte*>(&_x), sizeof(T) };
}
// implementation of physical I/O (not a functor, but tempting)
struct A {
enum class E1 : uint8_t { v1 = 10, v2, v3, v4 };
bool f(uint8_t _i1, gsl::span<std::byte> _buf = {}); // a - "in the end" they all call here
bool f(E1 _i1, gsl::span<std::byte> _buf = {}); // b
template<typename T, typename = std::enable_if_t< std::is_integral<T>::value > >
bool f(uint8_t _i1, T& _val); // c
template<typename T, typename = std::enable_if_t< std::is_integral<T>::value > >
bool f(E1 _i1, T& _val); // d
};
bool A::f(uint8_t _i1, gsl::span<std::byte> _buf)
{
std::cout << "f() uint8_t{" << (int)_i1 << "} with " << _buf.size() << " elements\n";
return true;
}
bool A::f(E1 _i1, gsl::span<std::byte> _buf)
{
std::cout << "f() E1{" << (int)_i1 << "} with " << _buf.size() << " elements\n\t";
return f((uint8_t)_i1, _buf);
}
template<class T, typename>
bool A::f(uint8_t _i1, T& _val)
{
std::cout << "template uint8_t\n\t";
return f(_i1, byte_span(_val));
}
template<class T, typename>
bool A::f(E1 _i1, T& _val)
{
std::cout << "template E1\n\t";
return f(_i1, byte_span(_val));
}
int main(){
A a = {};
std::array<std::byte, 1> buf;
long i = 2;
// regular function overloads
a.f(1, buf);
a.f(A::E1::v1, buf);
// template overloads
a.f(2, i);
a.f(A::E1::v2, i);
struct S { short i; };
// issue
//S s;
//a.f(3, s);
//a.f(A::E1::v3, s);
//// bonus - should use non-template overrides
//S sv[2] = {};
//a.f(5, sv);
//a.f(A::E1::v1, sv);
}
Details
The struct S
is a PoD and it is tempting to change the template's enable_if
to use the std::is_trivial
or std::is_standard_layout
. Unfortunately both these solutions "grab too much" and end up matching std::array
(even if they do fix the compilation error of the //issue
block).
The solution I have right now looks like a dead-end as my gut feeling is to start adding more template parameters and it seem to be getting very hairy very soon :(
Question
My goal is to achieve the following: use the class A
's bool f()
member function without too much syntactic overhead for any PoD (possibly including C arrays - see "bonus" in code) as shown in the body of main()
and no runtime function call overhead for types that are auto-convertible to gsl::span
(like std::array
and std::vector
).
If this is not achievable I'd like to know why.
I'm on MSVC 2017 with C++17 enabled.
Aucun commentaire:
Enregistrer un commentaire