Reputation: 718
I am trying to write a variadic function template to calculate the byte size of a struct
. This is going to be used in a network programming project I am working on. The first step I came up with this without the variadic template that works:
#include <cstdint>
#include <iostream>
struct Position
{
int32_t x;
int32_t y;
};
template<typename T>
inline std::size_t sizeT() { return sizeof(T); }
template<>
inline std::size_t sizeT<Position>() { return sizeT<int32_t>() + sizeT<int32_t>(); }
// if I change the definition of Position, I need to remember to change this function
int main(int argc, char* argv[])
{
std::cout << "int8_t:" << sizeT<int8_t>() << std::endl;
std::cout << "Position:" << sizeT<Position>() << std::endl;
return 0;
}
I compiled with g++
9.3.0
on my Ubuntu 20.04
laptop and it worked fine. However if I try to make it a variadic template then I ran into different compilation errors. The point of this variadic template is to be able to write like the following, so that the implementation doesn't depend on the explicit knowledge of x
and y
's types.
Here is bad code #1
#include <cstdint>
#include <iostream>
struct Position
{
int32_t x;
int32_t y;
};
template<typename T>
inline std::size_t sizeT() { return sizeof(T); }
template<>
inline std::size_t sizeT<Position>()
{
return sizeT<decltype(Position::x), decltype(Position::y)>();
}
template<>
inline std::size_t sizeT<Position>() { return sizeT<int32_t>() + sizeT<int32_t>(); }
int main(int argc, char* argv[])
{
std::cout << "int8_t:" << sizeT<int8_t>() << std::endl;
std::cout << "Position:" << sizeT<Position>() << std::endl;
return 0;
}
I got
error: ambiguous template specialization 'sizeT' for 'std::size_t sizeT()
note: candidate are: 'template std::size_t sizeT()'
note: template<class T, class... Ts> std::size_t sizeT()'
Bad code #2. If I put the variadic template at a different location:
#include <cstdint>
#include <iostream>
struct Position
{
int32_t x;
int32_t y;
};
template<typename T>
inline std::size_t sizeT() { return sizeof(T); }
template<>
inline std::size_t sizeT<Position>() { return sizeT<int32_t>() + sizeT<int32_t>(); }
template<>
inline std::size_t sizeT<Position>()
{
return sizeT<decltype(Position::x), decltype(Position::y)>();
}
int main(int argc, char* argv[])
{
std::cout << "int8_t:" << sizeT<int8_t>() << std::endl;
std::cout << "Position:" << sizeT<Position>() << std::endl;
return 0;
}
I got
error: call of overloaded 'sizeT<int8_t>()' is ambiguous
note candidate: 'std::size_t sizeT() [with T=signed char; std::size_t = long unsigned int]'
note candidate: 'std::size_t sizeT() [with T=signed char; Ts={}; std::size_t = long unsigned int]'
error: call of overloaded 'sizeT()' is ambiguous
note candidate: 'std::size_t sizeT() [with T=Position; std::size_t = long unsigned int]'
note candidate: 'std::size_t sizeT() [with T=Position; Ts={}; std::size_t = long unsigned int]'
I kinda understand case #2 but I don't understand #1. How can I achieve this? Thank you so much in advance.
Upvotes: 0
Views: 149
Reputation: 218323
You don't use variadic...
You might do (C++17)
template <typename ... Ts>
constexpr std::size_t sizeT() { return (0 + ... + sizeof(Ts)); }
template <>
constexpr std::size_t sizeT<Position>()
{
return sizeT<decltype(Position::x), decltype(Position::y)>();
}
But sizeT<Position, Position>
would return 2 * sizeof(Position)
instead of 2 * sizeT<Position>
(which are identical here).
You might do instead:
template <typename... Ts>
struct tag{};
template <typename T>
constexpr std::size_t sizeT(tag<T>) { return sizeof(T); }
template <typename ... Ts>
constexpr std::size_t sizeT(tag<Ts...>) { return (0 + ... + sizeT(tag<Ts>())); }
template <typename ... Ts>
constexpr std::size_t sizeT(tag<Ts>...) { return (0 + ... + sizeT(tag<Ts>())); }
constexpr std::size_t sizeT(tag<Position>)
{
return sizeT(tag<decltype(Position::x), decltype(Position::y)>());
}
Upvotes: 1