Skip to content

Instantly share code, notes, and snippets.

@deltaluca
Created April 12, 2017 22:17
Show Gist options
  • Save deltaluca/3d4dee58b69ea517285cae5252784a5b to your computer and use it in GitHub Desktop.
Save deltaluca/3d4dee58b69ea517285cae5252784a5b to your computer and use it in GitHub Desktop.
crazy templated smarter getnumberofbytes
#include <iostream>
#include <cstdint>
#include <vector>
#include <array>
#include <type_traits>
// default implementations of GetNumberOfBytesNoArgs / GetNumberOfBytesWithArgs
template <typename T, typename = int> struct GetNumberOfBytesNoArgs {};
template <typename T, typename = int> struct GetNumberOfBytesWithArgs {};
// type traits to determine if an implementation of GetNumberOfBytesNoArgs exists for the given type.
template <typename T, typename = int> struct no_args_get_size : std::false_type {};
template <typename T>
struct no_args_get_size<T,
typename std::enable_if<std::is_same<size_t,
decltype(GetNumberOfBytesNoArgs<T>::Get())>::value, int>::type
> : std::true_type {};
// define GetNumberOfBytesNoArgs<T> for all arithmetic types T (eg char, int, double)
template <typename T>
struct GetNumberOfBytesNoArgs<T, typename std::enable_if<std::is_arithmetic<T>::value, int>::type>
{
static constexpr size_t Get() { return sizeof(T); }
};
// define GetNumberOfBytesNoArgs for std::arrays where T has no-args get.
template <typename T, size_t N>
struct GetNumberOfBytesNoArgs<std::array<T, N>, typename std::enable_if<no_args_get_size<T>::value, int>::type>
{
static constexpr size_t Get()
{
return sizeof(uint32_t) + N * GetNumberOfBytesNoArgs<T>::Get();
}
};
// and the general case which requires args.
template <typename T, size_t N>
struct GetNumberOfBytesWithArgs<std::array<T, N>>
{
static size_t Get(const std::array<T, N>& xs)
{
size_t size = sizeof(uint32_t);
for (auto &x: xs)
{
size += GetNumberOfBytesWithArgs<T>::Get(x);
}
return size;
}
};
// define similarly for std vectors, a more optimal version when the contained type has no-args get.
template <typename T>
struct GetNumberOfBytesWithArgs<std::vector<T>, typename std::enable_if<no_args_get_size<T>::value, int>::type>
{
static size_t Get(const std::vector<T> &xs)
{
return sizeof(uint32_t) + GetNumberOfBytesNoArgs<T>::Get() * xs.size();
}
};
// and the case when it doesn't. (both versions require arg, so have to make the opposite case explicit)
template <typename T>
struct GetNumberOfBytesWithArgs<std::vector<T>, typename std::enable_if<!no_args_get_size<T>::value, int>::type>
{
static size_t Get(const std::vector<T> &xs)
{
size_t size = sizeof(uint32_t);
for (auto &x: xs)
{
size += GetNumberOfBytesWithArgs<T>::Get(x);
}
return size;
}
};
// a test case of something that requires args to know its size.
struct Test
{
size_t size;
};
template <>
struct GetNumberOfBytesWithArgs<Test>
{
static size_t Get(const Test& x)
{
return x.size;
}
};
// define wrapper functions (GetNumberOfBytes(arg?))
// defined such that GetNumberOfBytes(arg) will forward to GetNumberOfBytes<decltype(arg)>() if possible.
template <typename T, typename = int>
struct Wrapped
{
static size_t Get(const T &x) { return GetNumberOfBytesWithArgs<T>::Get(x); }
};
template <typename T>
struct Wrapped<T, typename std::enable_if<no_args_get_size<T>::value, int>::type>
{
static size_t Get(const T &) { return GetNumberOfBytesNoArgs<T>::Get(); }
};
template <typename T>
size_t GetNumberOfBytes(const T &x)
{
return Wrapped<T>::Get(x);
}
template <typename T>
typename constexpr std::enable_if<no_args_get_size<T>::value, int>::type GetNumberOfBytes()
{
return GetNumberOfBytesNoArgs<T>::Get();
}
int main()
{
static_assert(no_args_get_size<char>::value, "");
static_assert(no_args_get_size<double>::value, "");
static_assert(no_args_get_size<std::array<char, 4>>::value, "");
static_assert(!no_args_get_size<Test>::value, "");
static_assert(!no_args_get_size<std::array<Test, 4>>::value, "");
static_assert(GetNumberOfBytes<char>() == sizeof(char), "");
static_assert(GetNumberOfBytes<double>() == sizeof(double), "");
static_assert(GetNumberOfBytes<std::array<char, 4>>() == sizeof(char) * 4 + sizeof(uint32_t), "");
std::cout << GetNumberOfBytes<char>() << std::endl;
std::cout << GetNumberOfBytes<float>() << std::endl;
std::cout << GetNumberOfBytes('a') << std::endl;
std::cout << GetNumberOfBytes(10.f) << std::endl;
std::cout << GetNumberOfBytes(Test{10}) << std::endl;
std::cout << "---\n";
std::cout << GetNumberOfBytes<std::array<char, 4>>() << std::endl;
std::cout << GetNumberOfBytes<std::array<float, 4>>() << std::endl;
std::cout << GetNumberOfBytes(std::array<char, 4>{}) << std::endl;
std::cout << GetNumberOfBytes(std::array<float, 4>{}) << std::endl;
std::cout << GetNumberOfBytes(std::array<Test, 4>{{ {1}, {2}, {3}, {4} }}) << std::endl;
std::cout << "---\n";
std::cout << GetNumberOfBytes(std::vector<char>(10)) << std::endl;
std::cout << GetNumberOfBytes(std::vector<float>(10)) << std::endl;
std::cout << GetNumberOfBytes(std::vector<std::vector<char>>(10)) << std::endl;
std::cout << GetNumberOfBytes(std::vector<std::vector<float>>(10)) << std::endl;
std::cout << GetNumberOfBytes(std::vector<Test>{{10},{20}}) << std::endl;
std::cout << "---\n";
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment