Создайте boost :: type_erasure :: any из строки C, но сохраните как std :: string

Можно ли объявить boost::type_erasure::any таким образом, что конструирование и назначение из строкового литерала или char const* автоматически копирует строку в std::string и хранит это в boost::type_erasure::any объект?

По умолчанию 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;
}

Живая Демо.

1

Решение

Нет, any хранит все что угодно. const char* это что-нибудь.

Обратите внимание, что "hello"s является литералом типа std::string,

0

Другие решения

Я публикую ответ на свой вопрос в надежде уточнить предполагаемое использование boost::type_erasure::any не делая первоначальный вопрос слишком многословным.

Этот ответ показывает возможный обходной путь, скрывая boost::type_erasure::any за интерфейсом другого класса и предоставлением перегрузки метода установки для всех типов, конвертируемых в std::string, Эта перегрузка заботится о преобразовании указателей и массивов символов в std::string,

Я думаю, что одна дополнительная перегрузка не так уж и плоха, но в идеале я хотел бы избежать этого шаблона и сделать any Тип осведомлен о том, как конвертировать строки C. Это возвращает нас к моему оригинальный вопрос.

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

Живая Демо.

0

По вопросам рекламы [email protected]