How to pass const char[] array as constexpr template parameter using C++ STL library?
I have the following standard working code :C++ 17
template< int PathIndex, int PathLength, const char (path)[PathLength] >
constexpr const int findlastslash()
{
if constexpr( PathIndex < 1 || path[PathIndex] == '/' || path[PathIndex] == '\\' ) {
return PathIndex;
}
else {
return findlastslash<PathIndex - 1, PathLength, path>();
}
}
template< int PathLength, const char (path)[PathLength] >
constexpr const int startfindlastslash()
{
return findlastslash< PathLength - 1, PathLength, path >();
}
int main(int argc, char const *argv[]) {
static constexpr const char path[7] = "c/test";
static_assert( startfindlastslash< 7, path >() == 1, "Fail!" );
}
I want to stop writing/hardcoding the array size , i.e., make template metaprogramming infer the array size itself without having to write the size all over the place. For example, given something like the following:constexpr
7
constexpr const char[]
template< int PathIndex, int PathLength, const char (path)[PathLength] >
constexpr const int findlastslash()
{
if constexpr( PathIndex < 1 || path[PathIndex] == '/' || path[PathIndex] == '\\' ) {
return PathIndex;
}
else {
return findlastslash<PathIndex - 1, PathLength, path>();
}
}
template< const char (path)[PathLength] >
constexpr const int startfindlastslash()
{
return findlastslash< PathLength - 1, PathLength, path >();
}
template<int PathLength>
constexpr const char path[PathLength] = "c/test";
int main(int argc, char const *argv[]) {
static_assert( startfindlastslash< path >() == 1, "Fail!" );
}
Of course, the above code is completely invalid. However, it is easy to describe things.
How will you solve this problem? Would you replace one or ?constexpr const char path[7] = "c/test";
std::array
std::string_view
I try to build this with the following code :std::string_view
#include <string_view>
template< int PathIndex, std::string_view path >
constexpr const int findlastslash()
{
if constexpr( PathIndex < 1 || path[PathIndex] == '/' || path[PathIndex] == '\\' ) {
return PathIndex;
}
else {
return findlastslash<PathIndex - 1, path>();
}
}
template< std::string_view path >
constexpr const int startfindlastslash()
{
return findlastslash< path.length() - 1, path >();
}
int main(int argc, char const *argv[]) {
static constexpr std::string_view path{"c/test"};
static_assert( startfindlastslash< path >() == 1, "Fail!" );
}
But it doesn't compile:
g++ -o main.exe --std=c++17 test_debugger.cpp
test_debugger.cpp:3:43: error: ‘class std::basic_string_view<char>’ is not a valid type for a template non-type parameter template< int PathIndex, std::string_view path > ^~~~ test_debugger.cpp:14:28: error: ‘class std::basic_string_view<char>’ is not a valid type for a template non-type parameter template< std::string_view path > ^~~~ test_debugger.cpp: In function ‘int main(int, const char**)’: test_debugger.cpp:22:47: error: no matching function for call to ‘startfindlastslash<path>()’ static_assert( startfindlastslash< path >() == 1, "Fail!" ); ^ test_debugger.cpp:15:21: note: candidate: template<<typeprefixerror>path> constexpr const int startfindlastslash() constexpr const int startfindlastslash() ^~~~~~~~~~~~~~~~~~ test_debugger.cpp:15:21: note: template argument deduction/substitution failed: test_debugger.cpp:22:47: note: invalid template non-type parameter static_assert( startfindlastslash< path >() == 1, "Fail!" ); ^
clang++ -Xclang -ast-print -fsyntax-only --std=c++17 test_debugger.cpp > main.exe
test_debugger.cpp:3:43: error: a non-type template parameter cannot have type 'std::string_view' (aka 'basic_string_view<char>') template< int PathIndex, std::string_view path > ^ test_debugger.cpp:14:28: error: a non-type template parameter cannot have type 'std::string_view' (aka 'basic_string_view<char>') template< std::string_view path > ^ test_debugger.cpp:15:21: error: no return statement in constexpr function constexpr const int startfindlastslash() ^ test_debugger.cpp:22:20: error: no matching function for call to 'startfindlastslash' static_assert( startfindlastslash< path >() == 1, "Fail!" ); ^~~~~~~~~~~~~~~~~~~~~~~~~~ test_debugger.cpp:15:21: note: candidate template ignored: invalid explicitly-specified argument for template parameter 'path' constexpr const int startfindlastslash() ^ 4 errors generated.
Note: I'm not interested in the last slash of the string during the execution of the algorithm. I'm just using this silly example as an excuse to better learn what you can and can't do with constexpr template parameters.
For reference, an example of a (C++20) string literal as a non-type template parameter? I found this sample code using : ( https://godbolt.org/z/L0J2K2 ) to do some cool stuffC++ 20
template<unsigned N>
struct FixedString {
char buf[N + 1]{};
constexpr FixedString(char const* s) {
for (unsigned i = 0; i != N; ++i) buf[i] = s[i];
}
constexpr operator char const*() const { return buf; }
};
template<unsigned N> FixedString(char const (&)[N]) -> FixedString<N - 1>;
template<FixedString T>
class Foo {
static constexpr char const* Name = T;
public:
void hello() const;
};
int main() {
Foo<"Hello!"> foo;
foo.hello();
}
Do I really need to define my own class? Is there anything that (Standard Template Library) can be used to replace this common/simple task?FixedString
C++ STL
For reference, I found this nice related 3rd party library:
- https://github.com/irrequietus/typestring . _
C++11/14 strings for direct use in template parameter lists, template metaprogramming
- https://github.com/hanickadot/compile-time-regular-expressions
A Compile time PCRE (almost) compatible regular expression matcher.
I want to stop writing/hardcoding constexpr array size 7
In C++17 you can use it auto
as a non-template parameter and get rid of hardcoding 7
:
template <std::size_t PathIndex, const auto& path>
constexpr std::size_t findlastslash()
{
if constexpr (PathIndex < 1 || path[PathIndex] == '/' || path[PathIndex] == '\\') {
return PathIndex;
}
else {
return findlastslash<PathIndex - 1, path>();
}
}
template <const auto& path>
constexpr std::size_t startfindlastslash()
{
return findlastslash<std::extent_v<std::remove_reference_t<decltype(path)>> - 1, path >();
}
int main() {
static constexpr const char path[] = "c/test";
static_assert(startfindlastslash<path>() == 1, "Fail!" );
}
You can "protect" it auto
using SFINAE or concepts (C++20) :
template <typename T, std::size_t N>
constexpr std::true_type is_c_array_impl(const T(&)[N]) { return {}; }
template <typename T>
constexpr std::false_type is_c_array_impl(const T&) { return {}; }
template <typename T>
constexpr auto is_c_array() -> decltype(is_c_array_impl(std::declval<const T&>())) {return {};}
template <typename T>
concept CArrayRef = (bool) is_c_array<const T&>() && std::is_reference_v<T>;
template <std::size_t PathIndex, const auto& path>
constexpr std::size_t findlastslash() requires (CArrayRef<decltype(path)>)
{
if constexpr (PathIndex < 1 || path[PathIndex] == '/' || path[PathIndex] == '\\') {
return PathIndex;
}
else {
return findlastslash<PathIndex - 1, path>();
}
}
template <const auto& path>
constexpr std::size_t startfindlastslash() requires (CArrayRef<decltype(path)>)
{
return findlastslash<std::extent_v<std::remove_reference_t<decltype(path)>> - 1, path >();
}
Do I really need to define my own
FixedString
class? Doesn't the C++ STL (Standard Template Library) have anything useful for this common/simple task?
It appears that there is currently no equivalent to FixedString
.