2017-07-26 177 views
4

我有一些舊代碼使用非常類似於str_const描述的herehere來執行一些constexpr字符串操作。 str_const是Scott Schurr描述的一種文字類型,可以從字符串文字構造,因爲它具有const char (&)[]的模板構造函數。將`hana :: string`轉換爲`constexpr const char(&)[]`

我現在也有一些使用boost::hana的新代碼。

我希望能夠採取hana::string並創建一個引用它的str_const。最簡單的方法是將hana::string轉換爲constexpr const char (&)[]。 (實際上,在這一點上,這不是最簡單的方法,最簡單的方法當然是爲我的str_const實現添加一個新的模板構造函數。但是在這一點上,這個問題已經發生在它自己的生活中,我主要關心的是這可以用hana::string來完成。所以,讓我們假設我不能改變str_const執行。)

然而,在hanadocs的方式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', ""); 
}