I have this working code for C++ 17
standard:
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 would like to stop writing/hard coding the constexpr
array size 7
, i.e., make the template meta-programming deduce by itself the constexpr const char[]
array size instead of having to write the size everywhere. For example, given something looking like the following:
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 code just above is utterly invalid. However, it is a good approximation of a easy way to describe things.
How would you solve this problem? Would you replace constexpr const char path[7] = "c/test";
by a std::array
or std::string_view
?
I tried building this code using 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 does not 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 am not interested in finding the last slash on the string as the algorithm is doing. I just took this silly example as an excuse to learn better what can and cannot be done with constexpr template parameters.
For reference, on (C++20) String literals as non-type template parameters example? I found this example code doing something cool with C++ 20
: (https://godbolt.org/z/L0J2K2)
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 FixedString
class? The C++ STL
(Standard Template Library) does not have anything which can be used instead for this common/simple task?
For reference, I found this nice related third part libraries:
- https://github.com/irrequietus/typestring for
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.