For example I have this class:
template <typename T, U>
class TLVParser
{
public:
TLVParser(T value) : value_(std::move(value)) {}
void parse(const std::span<uint8_t> &buffer, size_t &offset)
{
if constexpr (std::is_arithmetic_v<T>)
{
return parsePrimitive(buffer, offset);
}
else if constexpr
constexpr(std::is_same_v<T, std::string> || std::is_same_v<T, std::string_view>)
{
return parseStringView(buffer, offset);
}
else if constexpr (std::is_same_v<T, std::span<U>> || std::is_same_v<T, std::vector<U>>)
{
return parseContainer(buffer, offset);
}
else
{
static_assert(always_false<T>::value, "Unsupported type for TLV parsing");
}
}
private:
T value_;
};
As I understand, string_view, std::string, std::vector and std::span has some common properties that I could make the same parse function. So is there a way to use if constexpr to check if this type is a container like this ones without writing a || for each one, and just using a concept to garantee that this container should have a specific propertie like capable of range loop?
My second question is can I make a typename for the std::span<uint8_t> parameter so I can use a std::vector, std::array or even uint8_t* or char* ?
As a couple of people already pointed out in the comments, determining what constitutes a "container" is often the issue. One way is checking if it supports "std::begin()" and "std::end()", which also handles C-style arrays, opposed to just checking for classes that have members "begin()" and "end()" (which would ignore C-style arrays).
Here's another technique however which I prefer for reasons I won't get into here (for C++17 and later though it can be slightly tweaked to handle earlier versions). It simply checks if it's a class that has an "iterator" or "const_iterator" alias (typedef), in addition to a C-style array. The use of the macro is ugly but it's just to create the type safe code that does the work of checking for "iterator" or "const_iterator" (and the macro can be used to create type safe "HasMemberType_?" classes to detect any other type alias as well if required, not just "iterator" or "const_iterator"). Concepts are also now available of course (in C++20) so you can also create a concept that just defers to "IsContainer_v" if you wish (see this below). Some of the "std" concept classes here can also likely be leveraged for those that want to implement something more modern (but I won't start tackling that here).
Anyway, the following code wouldn't qualify for the standard itself but works very well in practice (and yes, it handles "std::string" if required since it has the necessary iterator aliases).
Click here to run it.