我有以下代码
#include <boost\any.hpp>
#include <iostream>
#include <memory>
#include <map>
#include <string>
enum class tags { int_param, string_param };
class Parameters
{
public:
template <typename T>
void set(tags key, T value)
{
map_[key] = value;
}
template <typename T>
T get(tags key)
{
return boost::any_cast<T>(map_[key]);
}
private:
std::map<tags, boost::any> map_;
};
int main()
{
Parameters params;
params.set(tags::int_param, 42);
params.set(tags::string_param, "it works!");
int int_par = params.get<int>(tags::int_param);
std::string string_par = params.get<std::string>(tags::string_param);
std::cout << "Channel: " << int_par << std::endl;
std::cout << "Filename: " << string_par << std::endl;
}
这段代码有效,但我不喜欢提供模板类型,如果可能的话,我希望能够从地图中检索某些东西,而不必提供模板类型,即
int int_par = params.get(tags::int_param);
而不是
int int_par = params.get<int>(tags::int_param);
我理解通过使用boost :: any我们需要从任何类型转换为提供的类型,但有没有办法我们可以做到这一点而不必自己提供它,我们可以用其他方式推断它&gt; < / p>
答案 0 :(得分:3)
没有办法从any
中推断出任何东西,但可能有机会从声明中推断出某些内容。由于您的代码在any_cast
失败时已经抛出,我认为这对您来说不是问题。
您可以扩展(由于缺少虚拟析构函数而真的是一个坏主意)boost::any
或将其包装并提供模板化转换运算符。
#include <boost/any.hpp>
#include <iostream>
struct converting_any {
converting_any(boost::any* a) : a(a) {}
template<typename T>
operator T() { return boost::any_cast<T>(*a); } // dereference to get the throwing version
private:
boost::any* a; // wrapped any
};
int main()
{
int i = 3; std::string j = "asddf";
boost::any a = i;
int i2 = converting_any(&a);
try {
std::string j2 = converting_any(&a);
} catch(...) {
std::cout << "Failure." << std::endl;
}
a = j;
std::string j3 = converting_any(&a);
return 0;
}
答案 1 :(得分:3)
如果您将标签从枚举值更改为结构,您可以实现所需的行为,甚至是其他良好的功能,例如类型安全性以及我们对any_cast的使用不会抛出。
通过使用结构,我们可以定义简单的特征来获取每个标签的值类型,例如
enum class tag_id
{
int_param; // Id which represents int_param tag on std::map
}
struct int_param_t
{
using value_type = int;
static const tag_id id;
};
const tag_id int_param_t::id = tag_id::int_param;
现在我们可以使用这个特性来实现您想要的语法
template< typename Tag >
auto get( Tag ) -> typename Tag::valye_type
{
return boost::any_cast<typename Tag::valye_type>(map_[Tag::id]);
}
// ...
int a = parameters.get( int_param_t{} ); // Ok, returns int.
std::string b = parameters.get( int_param_t{} ); // Error, int is not convertible to std::string.
作为奖励,我们可以使用此特征来确保只有可转换为正确类型的类型与set
函数一起使用
template< typename Tag >
void set( Tag, typename Tag::value_type value )
{
map_[Tag::id] = value;
}
// ...
parameter.set( int_param_t{}, 0 ); // Ok, 0 is convertible to int.
parameter.set( int_param_t{}, "string" ); // Error, const char[] is not convertible to int.
为了使它更漂亮,我还会定义一些别名和常量,例如
// Helper alias to avoid writing typename everywhere.
template< typename T >
using param_type = typename T::value_type;
// Used to avoid having to create tag objects every function call.
constexpr int_param_t int_param{};
这是最终产品
#include <boost/any.hpp>
#include <iostream>
#include <memory>
#include <map>
#include <string>
namespace tags
{
enum class tag_id
{
int_param,
string_param
};
struct int_param_t
{
using value_type = int;
static const tag_id id;
};
const tag_id int_param_t::id = tag_id::int_param;
constexpr int_param_t int_param{};
struct string_param_t
{
using value_type = std::string;
static const tag_id id;
};
const tag_id string_param_t::id = tag_id::string_param;
constexpr string_param_t string_param{};
}
// Helper alias to avoid writing typename everywhere.
template< typename T >
using param_type = typename T::value_type;
class Parameters
{
public:
template <typename Tag>
void set(Tag, param_type< Tag > value)
{
map_[Tag::id] = value;
}
template <typename Tag>
auto get(Tag) -> param_type< Tag >
{
return boost::any_cast< param_type< Tag > >(map_[Tag::id]);
}
private:
std::map<tags::tag_id, boost::any> map_;
};
int main()
{
Parameters params;
params.set(tags::int_param, 42);
params.set(tags::string_param, "it works!");
int int_par = params.get(tags::int_param);
std::string string_par = params.get(tags::string_param);
std::cout << "Channel: " << int_par << std::endl;
std::cout << "Filename: " << string_par << std::endl;
}
答案 2 :(得分:2)
你可以返回一个引用包装器,它将转换推迟到需要的时候:
struct converter {
converter(boost::any & any) : any(any) {}
template <typename T>
operator T() {return boost::any_cast<T>(any);}
boost::any & any;
};
converter get(tags key)
{
return map_[key];
}
模板参数可以从转换为的类型推导出来,因此您的int
示例将在没有显式模板参数的情况下工作。
请注意,与您的版本一样,如果模板参数与变体的类型不匹配,则会失败,如您在尝试将const char *
转换为std::string
时的示例所示。