Using a templated function that accepts a fixed length array by reference, templated on the length of the array:
#include <iostream>
#include <array>
constexpr char arrayStr[] = "a,b,c";
template<size_t N>
constexpr size_t numFields(const char(&arrayStr)[N], char delim) {
size_t count = 1;
for (const auto& ch : arrayStr) {
if (ch == delim) {
++count;
}
}
return count;
}
using namespace std;
int main(int argc, char *argv[]) {
array<string,numFields(arrayStr,',')> x;
cout << x.size() << endl;
}
Templating arrows arrayStr to be a fixed-size array argument, allowing the range-based for loop.
EDIT
The OP asked in the comments about creating a class at compile time whose members include the string literal and its tokenization count (something was also mentioned about static class members, but I'm not clear on the use case). This was trickier! After some work, the above numFields function can be used together with something like this:
class Foo {
public:
template<typename T>
constexpr Foo(T&& str, char delim)
: _array(std::forward<T>(str)),
_count(numFields(_array,delim)) {
}
auto data() const {
return _array;
}
size_t size() const {
return _count;
}
private:
const char (&_array)[N];
const size_t _count;
};
template<typename T>
constexpr auto wrapArray(T&& str, char delim) -> Foo<sizeof(str)> {
return Foo<sizeof(str)>(std::forward<T>(str),delim);
}
constexpr auto wrappedArrayStr = wrapArray("a,b,c",',');
using namespace std;
int main(int argc, char *argv[]) {
cout << wrappedArrayStr.size() << endl;
cout << wrappedArrayStr.data() << endl;
}
I'm not certain that the perfect forwarding here is necessary, but I use it to forward the string literal argument to the class member. The helper function wrapArray prevents having to double-paste all the compile time string literals, i.e. avoiding constexpr Foo<sizeof("a,b,c")> wrappedArrayStr("a,b,c",',');.