C ++ 17 разделяет строку constexpr на запятую и имеет количество элементов во время компиляции? - PullRequest
5 голосов
/ 01 октября 2019

У меня есть список полей через запятую, представленный массивом символов во время компиляции:

constexpr static char arrayStr[] = "a,b,c";

Я хотел бы разделить на "," и иметь номерэлементы доступны во время компиляции. Псевдокод:

constexpr static size_t numFields = SPLIT(arrayStr, ",");

вернул бы 3.

Есть ли способ достичь этого с помощью C ++ 17?

Ответы [ 2 ]

4 голосов
/ 01 октября 2019

Я хотел бы преобразовать массив char в строку, затем разделить на «,» и получить количество элементов, доступных во время компиляции.

Если для «string» выозначает "std::string", это не constexpr, поэтому оно несовместимо с вычислением времени компиляции.

Если для "string" вы принимаете строку в стиле C, то char const *, и если выЗаинтересовавшись разделителями одного char, вы можете попробовать что-то следующим образом

#include <iostream>

constexpr static char arrayStr[] = "a,b,c";

constexpr std::size_t SPLIT (char const * str, char sep)
 {
   std::size_t  ret { 1u };

   while ( *str )
      if ( sep == *str++ )
         ++ ret;

   return ret;
 }

int main ()
 {
   constexpr auto numFields = SPLIT(arrayStr, ',');

   std::cout << numFields << std::endl;  // print 3
 }
1 голос
/ 01 октября 2019

Использование шаблонной функции, которая принимает массив фиксированной длины по ссылке, шаблонизированный по длине массива:

#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;
}

Стрелки-шаблоны arrayStr являются аргументом массива фиксированного размера, что позволяет на основе диапазонацикл for.

EDIT

ОП спросил в комментариях о создании класса во время компиляции, членами которого являются строковый литерал и счетчик его токенизации (что-то также упоминалосьнасчет статических учеников, но я не совсем понимаю вариант использования). Это было сложнее! После некоторой работы вышеприведенную функцию numFields можно использовать вместе с чем-то вроде этого:

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;
}

Я не уверен, что здесь необходима идеальная пересылка, но я использую ее для пересылки строкового литералааргумент члена класса. Вспомогательная функция wrapArray предотвращает необходимость двойной вставки всех строковых литералов времени компиляции, т.е. избегает constexpr Foo<sizeof("a,b,c")> wrappedArrayStr("a,b,c",',');.

...