Преобразовать `hana::string` в`constexpr const char (&)[]`

У меня есть старый код, который использует что-то очень похожее на str_const описано здесь и здесь, чтобы сделать некоторые манипуляции строк constexpr. str_const литеральный тип, описанный Скоттом Шурром, который может быть построен из строкового литерала, потому что он имеет конструктор шаблона из const char (&)[],

Теперь у меня есть новый код, использующий boost::hana,

Я хотел бы иметь возможность взять hana::string и создать str_const это относится к этому. Самый простой способ сделать это - преобразовать hana::string к constexpr const char (&)[], (На самом деле, на данный момент это не самый простой способ, самый простой способ, конечно, это добавить новый конструктор шаблона в мой str_const реализация. Но на данный момент вопрос приобрел собственную жизнь, и я в основном заинтересован в том, можно ли это сделать с hana::string, Итак, давайте предположим, что мне не разрешено менять str_const реализация.)

Однако в hana документы способ конвертировать hana::string к строке времени выполнения hana::to<const char *>,

Оптимистично, я пробовал различные формы hana::to<const char (&)[hana::length(...)]> (...) но это вызывает статические утверждения в hana терпеть неудачу.

Другой вариант, предложенный hana документы использовать hana::unpack а затем вставьте символы в массив самостоятельно. Я написал этот код

template <typename T, size_t N>
struct array {
  T arr[N];
};

struct char_packer {
  template <typename... Ts>
  constexpr auto operator()(Ts... ts) -> array<const char, sizeof...(ts) + 1> {
    return array<const char, sizeof...(ts) + 1>{{ ts... , 0 }};
  }
};

template <typename S>
struct string_keeper {
  static constexpr auto my_array = hana::unpack(S{}, char_packer{});
};

template <int N>
using char_arr = const char [N];

template <typename S>
constexpr auto to_string_literal(S &&) -> const char_arr<decltype(hana::length(S{}))::value + 1> & {
  return string_keeper<S>::my_array.arr;
}

Я думаю, что это почти работает, по крайней мере, компилируется. Но если ссылки также используются во время выполнения, то происходит сбой с ошибкой компоновщика: undefined reference to ... string_keeper<boost::hana::string<(char)97> >::my_array,

(На самом деле, я думаю, что понимаю, почему это проблема с ODR, и если я подумаю над этим немного дольше, я могу вспомнить, как это исправить... не уверен...)

Интуитивно я чувствую, что должен быть способ сделать это. Так как, hana уже позволяет мне конвертировать hana::string в constexpr const char * где указатель указывает на массив, который я хочу. На самом деле это даже наводит на мысль о том, что может быть злой вариант, когда я пытаюсь const char * вернуться к (&)[] тип, хотя это также кажется, что это потребует делать вещи, которые не будут разрешены в constexpr функции. В любом случае, если hana может сделать этот массив, то, конечно, я тоже могу, или как-то убедить его дать мне более точно.

Есть ли способ исправить мой код выше? Есть ли более простой способ сделать это в течение hana что я упустил из виду? Это действительно невозможно по какой-то причине?

1 ответ

Решение

Еще одна проблема заключается в том, что при возврате из функции необработанный массив символов будет преобразован в указатель. Я бы предложил построить str_const объект в контексте вашей функции, которая, я считаю, выполняет ваше намерение создать str_const без изменения его интерфейса.

В следующем примере используется шаблон переменной верхнего уровня для создания массива, который hana::string реализация использует:

#define BOOST_HANA_CONFIG_ENABLE_STRING_UDL
#include <boost/hana.hpp>
#include <stdexcept>

namespace hana = boost::hana;
using namespace hana::literals;

class str_const {
    const char * const p_;
    const std::size_t sz_;
public:
    template <std::size_t N>
    constexpr str_const( const char( & a )[ N ] )
    : p_( a ), sz_( N - 1 ) {}
    constexpr char operator[]( std::size_t n ) const {
        return n < sz_ ? p_[ n ] : throw std::out_of_range( "" );
    }
    constexpr std::size_t size() const { return sz_; }
};

template <char ...c>
constexpr char string_storage[sizeof...(c) + 1] = {c..., '\0'};

struct to_str_const_helper {
  template <typename ...Ts>
  constexpr auto operator()(Ts...) {
    return str_const(string_storage<Ts::value...>);
  }
};
template <typename S>
constexpr auto to_str_const(S) {
  return hana::unpack(S{}, to_str_const_helper{});
}

int main()
{
  constexpr str_const str = to_str_const("foo"_s);
  static_assert(str[0] == 'f', "");
  static_assert(str[1] == 'o', "");
  static_assert(str[2] == 'o', "");
}
Другие вопросы по тегам