Created
April 12, 2017 22:17
-
-
Save deltaluca/3d4dee58b69ea517285cae5252784a5b to your computer and use it in GitHub Desktop.
crazy templated smarter getnumberofbytes
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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