I am building a P2P networking library that allows users to directly send instantiated objects. As such, I need a way to uniquely identify classes.
Reading through type_info documentation did not help me, as I only learnt they were implementation defined, and that type_info::hash_code "can also change between invocations of the same program."
I did a piece of code to do that, using the preprocessor. It works quite well but is extremely slow to compile. Do you have any ideas on how I could increase the compilation speed (note: my code must be C++14-compliant)?
#include <cstdint>
#include <string>
#include <utility>
#include <tuple>
#define BREEP_DECLARE_TYPE(T) \
namespace breep { namespace detail { \
template <> \
struct networking_traits_impl<T> { \
const std::string universal_name = std::string(#T); \
}; \
}}
#define BREEP_DECLARE_TEMPLATE(TType) \
namespace breep { namespace detail { \
template <typename... T> \
struct networking_traits_impl<TType<T...>> { \
networking_traits_impl(); \
const std::string universal_name = std::string(#TType"<") + networking_traits_impl<typename std::tuple_element<0, std::tuple<T...>>::type>().universal_name + detail::identifier_from_tuple<detail::remove_type<0, T...>>().value + ">"; \
}; \
template <typename... T> \
networking_traits_impl<TType<T...>>::networking_traits_impl() {}\
}}
namespace breep {
namespace detail {
uint64_t hash(const std::string& str);
template <typename>
struct networking_traits_impl {};
template<typename T>
struct networking_traits_impl<T&> {
const std::string universal_name = networking_traits_impl<T>().universal_name;
};
template<typename T>
struct networking_traits_impl<T*> {
const std::string universal_name = networking_traits_impl<T>().universal_name;
};
template<typename T>
struct networking_traits_impl<const T> {
const std::string universal_name = networking_traits_impl<T>().universal_name;
};
template<typename T>
struct networking_traits_impl<T&&> {
const std::string universal_name = networking_traits_impl<T>().universal_name;
};
template<typename T>
struct networking_traits_impl<volatile T> {
const std::string universal_name = networking_traits_impl<T>().universal_name;
};
}
template <typename>
struct universal_name {};
template <typename T>
struct type_traits {
static const std::string& universal_name(){
static const std::string name = detail::networking_traits_impl<T>().universal_name;
return name;
}
static uint64_t hash_code() {
static const uint64_t hash = detail::hash(universal_name());
return hash;
}
};
namespace detail {
// sdbm's hash algorithm, gawk's implementation.
uint64_t hash(const std::string& str) {
uint64_t hash_code = 0;
for (std::string::size_type i = str.size() ; i-- ;) {
if (str[i] != '>' && str[i] != ',' && str[i] != ' ' && (str[i] != ':' || str[i+1] != ':')) {
hash_code = str[i] + (hash_code << 6) + (hash_code << 16) - hash_code;
}
}
return hash_code;
}
template<std::size_t N, typename Tuple, std::size_t... Idx>
auto remove (std::index_sequence<Idx...>) ->
decltype(std::tuple_cat(std::declval<std::conditional_t<(N == Idx),
std::tuple<>,
std::tuple<typename std::tuple_element<Idx, Tuple>::type>>>()...
));
template <std::size_t N, typename... Args>
using remove_type = decltype(detail::remove<N,std::tuple<Args...>>(std::index_sequence_for<Args...>{}));
template<typename... T>
struct identifier_from_tuple {
};
template<>
struct identifier_from_tuple<std::tuple<>> {
static const std::string value;
};
const std::string identifier_from_tuple<std::tuple<>>::value = "";
template<typename... T>
struct identifier_from_tuple<std::tuple<T...>> {
static const std::string value;
};
template<typename... T>
const std::string identifier_from_tuple<std::tuple<T...>>::value =
"," + networking_traits_impl<typename std::tuple_element<0, std::tuple<T...>>::type>().universal_name +
identifier_from_tuple<remove_type<0, T...>>::value;
}
}
If you want to see the full source, you may find it on GitHub.