2017-03-16 38 views
1

是否可以聲明boost::type_erasure::any以構建和分配字符串文字或char const*自動將該字符串複製到std::string並將該字符串存儲在boost::type_erasure::any的對象?從C字符串構建boost :: type_erasure :: any,但存儲爲std :: string

默認情況下,boost::type_erasure::any只存儲字符串指針。

目的是避免錯誤的來源,當我any類型的用戶的字符串指針分配給它,假設一個副本將被製成(如std::string那樣),則該字符串的壽命結束我的any被讀取之前,導致崩潰。

例子:

#include <boost/type_erasure/operators.hpp> 
#include <boost/type_erasure/any.hpp> 
#include <boost/type_erasure/any_cast.hpp> 
#include <boost/type_erasure/relaxed.hpp> 
#include <boost/mpl/vector.hpp> 

#include <iostream> 

namespace te = boost::type_erasure; 

using my_any = te::any< boost::mpl::vector< 
    te::copy_constructible<>, 
    te::destructible<>, 
    te::typeid_<>, 
    te::relaxed 
    /* I believe some changes here would do the trick */ 
    >>; 

using namespace std; 

int main() 
{ 
    // Store an std::string by explicitly calling string constructor. 
    my_any a = string("abc"); 

    // The following should copy-construct an std::string too but it just stores 
    // the string pointer. 
    my_any b = "abc"; 

    // Works as expected. 
    cout << te::any_cast<string>(a) << endl; 

    // This crashes because the underlying type of b is not std::string. 
    // With some changes to the my_any type this shouldn't crash anymore. 
    cout << te::any_cast<string>(b) << endl; 
} 

Live Demo.

回答

0

沒有,any商店什麼。 const char*是什麼。

請注意,"hello"sstd::string類型的文字。

+0

你應該提到的是'「你好」 s'作品僅在C++ 14及以上版本中。 –

+0

我希望沒有「s」後綴,因爲它只是調用'std :: string'構造函數的語法糖。我們的目的是爲了避免my_any用戶分配字符串指針時出現錯誤,假定會做一個副本(比如'std :: string'),然後指針的生命週期在'my_any'之前結束閱讀,導致崩潰。 – zett42

+0

@ zett42如果沒有首先檢查它或證明它是那種類型,你永遠不應該得到任何類型的類型。你永遠不應該把一個類型放在任何一箇中,而不要讓這個人把它拿出來證明它是什麼。如果你想要一個枚舉類型的集合,請考慮variant類型。如果你想支持任何必須可以轉換爲字符串的任何東西(或者甚至必須可選地將其轉換爲帶有運行時檢查失敗的字符串),那麼你可能會這樣做。 – Yakk

0

我發佈了我自己的問題的答案,希望澄清boost::type_erasure::any的預期用途,而不會使原始問題太冗長。

此答案通過將boost::type_erasure::any隱藏在另一個類的接口後面併爲可轉換爲std::string的所有類型提供setter方法的超載來顯示可能的解決方法。這個過載需要注意將char指針和char數組轉換爲std::string

我認爲一個額外的重載並不壞,但理想情況下,我想避免該樣板,並使any類型知道如何轉換C字符串。這使我們回到我的original question

#include <iostream> 
#include <unordered_map> 
#include <string> 
#include <type_traits> 

#include <boost/type_erasure/operators.hpp> 
#include <boost/type_erasure/any.hpp> 
#include <boost/type_erasure/any_cast.hpp> 
#include <boost/type_erasure/relaxed.hpp> 
#include <boost/mpl/vector.hpp> 

namespace te = boost::type_erasure; 

// A class to store attributes of any type by name. 
class Attributes 
{ 
public: 
    using Key = std::string; 

    // A type that can store any value (similar to std::any). 
    using AnyValue = te::any< boost::mpl::vector< 
     te::copy_constructible<>, 
     te::destructible<>, 
     te::typeid_<>, 
     te::relaxed 
     >>; 

    // Overload for all types that ain't strings. 
    template< typename T > 
     std::enable_if_t< !std::is_convertible<T, std::string>::value, 
    void > SetAttr(Key const& name, T&& value) 
    { 
     m_attr.insert_or_assign(name, std::forward<T>(value)); 
    } 

    // Convert to std::string for all convertible types 
    // (char pointer and char array included). 
    template< typename T > 
     std::enable_if_t< std::is_convertible<T, std::string>::value, 
    void > SetAttr(Key const& name, T&& value) 
    { 
     m_attr.insert_or_assign(name, std::string(std::forward<T>(value))); 
    } 

    template< typename T > 
    T GetAttr(Key const& name) const 
    { 
     return te::any_cast<T>(m_attr.at(name)); 
    } 

private: 
    std::unordered_map<Key, AnyValue> m_attr; 
}; 

下面的例子演示瞭如何不同類型的字符串可以傳遞給Attributes::SetAttr()並通過Attributes::GetAttr<std::string>()一般查詢:

using namespace std; 

Attributes a; 
// Works even w/o special care. 
a.SetAttr("key1", string("foo")); 

// Without the SetAttr() overload for strings, user would have to remind 
// to cast back to char const* when calling MyClass::GetAttr(). 
a.SetAttr("key2", "bar"); 

// Without the SetAttr() overload for strings, a later call to GetAttr() 
// would cause a crash because we are passing pointers to temporary objects. 
{ 
    // test arrays 
    char temp1[] = { 'b', 'a', 'z', 0 }; 
    a.SetAttr("key3", temp1); 
    char const temp2[] = { 'b', 'i', 'm', 0 }; 
    a.SetAttr("key4", temp2); 

    // test pointers 
    a.SetAttr("key5", &temp1[0]); 
    a.SetAttr("key6", &temp2[0]); 
} 

try 
{ 
    // When getting a string attribute we no longer have to care about how it was 
    // passed to SetAttr(), we can simply cast to std::string in all cases. 
    cout << "'" << a.GetAttr<string>("key1") << "'" << endl; 
    cout << "'" << a.GetAttr<string>("key2") << "'" << endl; 
    cout << "'" << a.GetAttr<string>("key3") << "'" << endl; 
    cout << "'" << a.GetAttr<string>("key4") << "'" << endl; 
    cout << "'" << a.GetAttr<string>("key5") << "'" << endl; 
    cout << "'" << a.GetAttr<string>("key6") << "'" << endl; 
} 
// boost::type_erasure::bad_any_cast or std::out_of_range 
catch(std::exception& e) 
{ 
    cout << "Error: " << e.what() << endl; 
} 

Live Demo.

+1

'SetAttr'應該用'T &&'而不是'T const&'來允許移入(假設你的版本支持它) – Yakk

+0

@Yakk謝謝,我已經根據你的建議更新了代碼。 – zett42

相關問題