To avoid disturbing, some of the namespaces and implementations may be filter out
There’s a component I’m struggling to design in C++17, who’s major function is to serialize some important details about the local type system into a raw buffer for adaptive data interpreting purpose across platforms.
The class TypeSystem
should provide a static template function called Dictate<T>
who creates the TypeDesc
of T
also TypeDesc
s of those types associated with T
should be created at once.
When I try to compile the code, compiler just keep giving me the error message as follow:
In file included from /home/ezr/Projects/libmos/.side/generated/src/mos/portable/type_desc.cpp:8:
portable/type_system.h: In instantiation of ‘static std::shared_ptr<const portable::TypeDesc> portable::TypeSystem::Dictate() [with T = const portable::TypeDesc]’:
portable/type_system.h:130:43: required from ‘static void portable::type<std::weak_ptr<_Tp> >::dictate(portable::TypeDesc&) [with Tp = const portable::TypeDesc]’
portable/type_system.h:67:21: required from ‘static std::shared_ptr<const portable::TypeDesc> portable::TypeSystem::Dictate() [with T = std::weak_ptr<const portable::TypeDesc>]’
portable/type_system.h:143:43: required from ‘static void portable::type<std::vector<_Tp, _Alloc> >::dictate(portable::TypeDesc&) [with Tp = std::weak_ptr<const portable::TypeDesc>; Alloc = std::allocator<std::weak_ptr<const portable::TypeDesc> >]’
portable/type_system.h:67:21: required from ‘static std::shared_ptr<const portable::TypeDesc> portable::TypeSystem::Dictate() [with T = std::vector<std::weak_ptr<const portable::TypeDesc> >]’
generated/src/portable/type_desc.cpp:27:69: required from here
portable/type_system.h:63:28: error: incomplete type ‘mos::portable::type<const portable::TypeDesc, void>’ used in nested name specifier
63 | desc->align = type<T>::align;
| ^~~~~
/home/ezr/Projects/libmos/include/mos/portable/type_system.h:64:27: error: incomplete type ‘portable::type<const portable::TypeDesc, void>’ used in nested name specifier
64 | desc->size = type<T>::size;
| ^~~~
/home/ezr/Projects/libmos/include/mos/portable/type_system.h:65:35: error: incomplete type ‘mos::portable::type<const portable::TypeDesc, void>’ used in nested name specifier
65 | desc->compact_size = type<T>::compact_size;
| ^~~~~~~~~~~~
/home/ezr/Projects/libmos/include/mos/portable/type_system.h:67:21: error: incomplete type ‘mos::portable::type<const portable::TypeDesc, void>’ used in nested name specifier
67 | type<T>::dictate(*desc);
I tried, to split the declaration and definition, the problem still there, help me please…
Here comes details:
The descriptor structure TypeDesc
records the necessary information of one named type and the weak pointer to the descriptors of types associated with this type, if some.
/// portable/type_desc.h
struct TypeDesc {
TypeCategory category;
size_t align;
size_t size;
size_t compact_size;
size_t count;
std::string name;
std::vector<std::weak_ptr<TypeDesc const>> subtypes;
std::vector<std::string> symbols;
std::vector<int64_t> values;
};
Do you see the field named subtypes
! That’s the criminal who involves this problem ! The function TypeSystem::Dictate<T>
calls the function type<T>::dictate
to fill up the TypeDesc
with type specific details. Field subtypes
makes the functions calls recursive when T
is TypeDesc
. (Maybe any other circular definition like this out there).
Anyway, let’s introduce the definition of TypeSystem
:
/// portable/type_system.h
class TypeSystem {
public:
static std::shared_ptr<TypeSystem> Create() {
return std::shared_ptr<TypeSystem>(new TypeSystem());
}
static std::shared_ptr<TypeSystem> Local() {
static auto instance = Create();
return instance;
}
template <typename T>
static std::shared_ptr<TypeDesc const> Dictate() {
const auto name = type<T>::name();
auto local = Local();
std::lock_guard lock(local->mutex_);
auto it = local->types_.find(name);
if (it != local->types_.end()) return it->second;
auto desc = std::make_shared<TypeDesc>();
desc->name = name;
desc->align = type<T>::align;
desc->size = type<T>::size;
desc->compact_size = type<T>::compact_size;
local->types_.emplace(name, desc);
type<T>::dictate(*desc);
return desc;
}
private:
TypeSystem() = default;
private:
std::map<std::string, std::shared_ptr<TypeDesc>> types_;
std::recursive_mutex mutex_;
};
There’s a class template portable::type<T>
, each specialization of this class provides some necessary information and methods about type T
.
/// portable/type.h
template <typename T, typename = void>
struct type;
Here are some necessary implementation of this template class associated with this problem.
/// portable/arithmetic.inl
template <typename T>
struct type<T, std::enable_if_t<std::is_arithmetic_v<T>>> {
static constexpr size_t align = alignof(T);
static constexpr size_t size = sizeof(T);
static constexpr size_t compact_size = size;
static void dictate(TypeDesc& desc);
static std::string name();
};
/// portable/vector.inl
template <typename Tp, typename Alloc>
struct type<std::vector<Tp, Alloc>> {
using T = std::vector<Tp, Alloc>;
static constexpr size_t align = alignof(size_t);
static constexpr size_t size = sizeof(size_t);
static constexpr size_t compact_size = sizeof(size_t);
static void dictate(TypeDesc& desc);
static std::string name();
};
/// portable/pointer.inl
template <typename Tp>
struct type<std::weak_ptr<Tp>> {
using T = std::weak_ptr<Tp>;
static constexpr size_t align = alignof(size_t);
static constexpr size_t size = sizeof(size_t);
static constexpr size_t compact_size = sizeof(size_t);
static void dictate(TypeDesc& desc);
static std::string name();
};
/// portable/type_system.h
template <typename T>
inline void type<T, std::enable_if_t<std::is_arithmetic_v<T>>>::dictate(
TypeDesc& desc) {
using deacy = std::decay_t<T>;
if constexpr (std::is_same_v<deacy, bool>) {
desc.category = TypeCategory::kBoolean;
} else if constexpr (std::is_integral_v<deacy>) {
if constexpr (std::is_unsigned_v<deacy>)
desc.category = TypeCategory::kUnsigned;
else
desc.category = TypeCategory::kInteger;
} else if constexpr (std::is_floating_point_v<deacy>) {
desc.category = TypeCategory::kNumber;
}
}
/// portable/type_system.h
template <typename Tp, typename Alloc>
inline void type<std::vector<Tp, Alloc>>::dictate(TypeDesc& desc) {
desc.category = TypeCategory::kVector;
desc.subtypes = {TypeSystem::Dictate<Tp>()};
}
/// portable/type_system.h
template <typename Tp>
inline void type<std::weak_ptr<Tp>>::dictate(TypeDesc& desc) {
desc.category = TypeCategory::kWeakPtr;
desc.subtypes = {TypeSystem::Dictate<Tp>()};
}
/// portable/type_system.h
template <typename T>
inline std::string type<T, std::enable_if_t<std::is_arithmetic_v<T>>>::name() {
return core::QualifiedName<T>();
}
/// portable/type_system.h
template <typename Tp, typename Alloc>
inline std::string type<std::vector<Tp, Alloc>>::name() {
return "std::vector<" + type<Tp>::name() + ">";
}
/// portable/type_system.h
template <typename Tp>
inline std::string type<std::weak_ptr<Tp>>::name() {
return "std::weak_ptr<" + type<Tp>::name() + ">";
}
Of course, the implementation of type<TypeDesc
. This implementation is generated by scripts according to the structure definition, and the same for all other custom structures.
/// generated/include/portable/type_type_desc.h
template<>
struct type<portable::TypeDesc> {
using T = portable::TypeDesc;
static constexpr size_t align = align_of<...>();
static constexpr size_t size = size_of<align, ...>();
static constexpr size_t compact_size = compact_size_of<...>();
static void dictate(TypeDesc& desc);
static std::string name();
};
/// generated/src/portable/type_desc.cpp
void type<portable::TypeDesc>::dictate(TypeDesc& desc) {
desc.category = TypeCategory::kStruct;
desc.subtypes = {
TypeSystem::Dictate<decltype(portable::TypeDesc::category)>(),
TypeSystem::Dictate<decltype(portable::TypeDesc::align)>(),
TypeSystem::Dictate<decltype(portable::TypeDesc::size)>(),
TypeSystem::Dictate<decltype(portable::TypeDesc::compact_size)>(),
TypeSystem::Dictate<decltype(portable::TypeDesc::count)>(),
TypeSystem::Dictate<decltype(portable::TypeDesc::name)>(),
TypeSystem::Dictate<decltype(portable::TypeDesc::subtypes)>(),
TypeSystem::Dictate<decltype(portable::TypeDesc::symbols)>(),
TypeSystem::Dictate<decltype(portable::TypeDesc::values)>(),
};
desc.symbols = {
"category",
"align",
"size",
"compact_size",
"count",
"name",
"subtypes",
"symbols",
"values",
};
}
std::string type<portable::TypeDesc>::name() {
return core::QualifiedName<portable::TypeDesc>();
}
That’s all I know right now. Since many of the specializations still have the template<...>
in signature, I cannot put them into a .cpp
file to solve this problem.
Please help me find the right structure to organize these implementations to be compiled with no error >_<
王雨泽 is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.