繁体   English   中英

模板转换运算符和 boost::any 或 std::any

[英]Template cast operator and boost::any or std::any

我写了一个通用的 class 来为任何 class 提供一个简单的基于 JSON 的初始化。 在我想将它应用到包含枚举的 class 之前,它就像一种魅力。

my base class first parse the JSON, find the sub object of the JSON that have the same name than the derived class (I use a CRTP...), and build a std::map<std::string, boost::any> _settings; 其中键是字段名称, boost::any 包含字符串、整数、双精度甚至数字数组。

我派生的 class 只需要像下面的示例一样实现更新 function :

class testJsonUpdate : public JsonSettingsCustomizer<testJsonUpdate>
{
public:
  static const std::string class_name;

  testJsonUpdate(const std::string& settings) {
    _test_int = 0;
    _test_vector = { 0.0f };

    ParseAndUpdate(settings);
  }
  ~testJsonUpdate() {}

  int Update()
  {
    UPDATE_VALUE(testJsonUpdate, _test_int);
    UPDATE_VALUE(testJsonUpdate, _test_vector);

    return 0;
  }

public:
  uint32_t _test_int;
  std::vector<float> _test_vector;
};
const std::string testJsonUpdate::class_name = "testJsonUpdate";

UPDATE_VALUE(testJsonUpdate, _test_int); 是一个宏,它的扩展使用以下代码。 问题是当我的派生 class 有一个 emun 成员时我该怎么办。 在这种情况下,boost::any 值是 integer,现在使用 T = "MYCLASS::enum type" 调用explicit operator T()会触发异常,因为 boost::any_cast 从 int 到 my枚举类型!

有没有办法编写一个转换运算符来提升::any cast 用来解决这个问题?

template<typename CALLER>
struct Value
{
std::string _key;
boost::any _value;

template<typename T>
explicit operator T() const
{
  try
  {
    return boost::any_cast<T>(_value); // <== where it trigger exception
  }
  catch (...)
  {
    throw std::logic_error(CALLER::class_name + ": config string parsing issue");
  }
}

template<typename T>
static Value<T> RetreiveValue(const std::map<std::string, boost::any> & settings, const   std::string & key)
{
  return{ key, settings.find(key)->second };
 } // RetreiveValue

#define UPDATE_VALUE(caller, member) \
key = BUILD_KEY(caller, member); \
if (_settings.end() != _settings.find(key)) \
{ \
   member = (decltype(member)) RetreiveValue<caller>(_settings, key); \
}

演示该问题的完整示例,在https://www.onlinegdb.com/online_c++_compiler和 C++17 上进行了测试(以获得 std::any 支持)

#include <iostream>
using namespace std;

#include <string>
#include <map>
#include <vector>
#include <memory>
#include <any>


  template<typename CALLER>
  struct Value
  {
    std::string _key;
    std::any _value;

    template<typename T>
    explicit operator T() const
    {
      try
      {
        return std::any_cast<T>(_value);
      }
      catch (...)
      {
        //throw std::logic_error(CALLER::class_name + ": config string parsing issue");
        throw;
      }
    }
  }; // Value

  template<typename T>
  static Value<T> RetreiveValue(const std::map<std::string, std::any> & settings, const std::string & key)
  {
    return{ key, settings.find(key)->second };
  } // RetreiveValue

    // ----------------------------------------------------------------------------
  template <typename CALLER, typename T> const std::string buildPrefix(const T &elt)
  {
    throw std::logic_error(CALLER::class_name + ": config string parsing issue");
  } // buildName

  template <typename CALLER> const std::string buildPrefix(const bool &elt) { return "b"; }
  template <typename CALLER> const std::string buildPrefix(const std::string &elt) { return "s"; }
  template <typename CALLER> const std::string buildPrefix(const int &elt) { return "i"; }


#define ADD_M_PREFIX(member) m ## member

#define BUILD_KEY(caller, member) buildPrefix<caller>(member) + #member;
#define BUILD_KEY_WITH_M_PREFIX(caller, member) buildPrefix<caller>( ADD_M_PREFIX(member) ) + #member;
#define UPDATE_VALUE(caller, member) \
key = BUILD_KEY(caller, member); \
if (_settings.end() != _settings.find(key)) \
{ \
  member = (decltype(member)) RetreiveValue<caller>(_settings, key); \
}

#define UPDATE_VALUE_WITH_M_PREFIX(caller, member) \
key = BUILD_KEY_WITH_M_PREFIX(caller, member); \
if (_settings.end() != _settings.find(key)) \
{ \
  ADD_M_PREFIX(member) = (decltype(ADD_M_PREFIX(member))) RetreiveValue<caller>(_settings, key); \
}

  template<typename T>
  class JsonSettingsCustomizer {
  public:
    JsonSettingsCustomizer() : _label(T::class_name) { }
    virtual ~JsonSettingsCustomizer() {}

    int ParseAndUpdate(const std::string& settings) {
      //JSON Parsing to map

      //fake data to test
      _settings["i_integer"] = (int)(1);
      _settings["s_msg"] = (std::string)("hello world");
      _settings["i_enum_value"] = (int)(1);

      T& derived = static_cast<T&>(*this);
      auto ret = derived.Update();

      return ret;
    }

  protected:
    std::map<std::string, std::any>  _settings;
    std::string key;
    std::string _label;

  };
  /************************************************************************************************/
  //    END TOOLING
  /************************************************************************************************/

  typedef enum : int  {
     enum_one = 1,
     enum_two = 2
  } ENUM_TYPE;
//extention for the new ENUM_TYPE
template<typename CALLER> const std::string buildPrefix(const ENUM_TYPE& elt) { return "i"; }

class testJsonUpdate : public JsonSettingsCustomizer<testJsonUpdate>
  {
    public:
      static const std::string class_name;

      testJsonUpdate(const std::string& settings)  {
            ParseAndUpdate(settings);
      }
      ~testJsonUpdate() {}

      int Update()
      {
        UPDATE_VALUE(testJsonUpdate, _integer);
        UPDATE_VALUE(testJsonUpdate, _msg);
        //UPDATE_VALUE(testJsonUpdate, _enum_value); // uncomment to break on the bad cast exception

        return 0;
      }

    public:
      int _integer;
      std::string _msg;
      ENUM_TYPE _enum_value;
  };
  const std::string testJsonUpdate::class_name = "testJsonUpdate";

int main() {
    // your code goes here
    testJsonUpdate o(".... JSON .... ");
    std::cout << o._integer << std::endl;
    std::cout << o._msg << std::endl;
    return 0;
}

我对 boost 不熟悉,所以我将使用标准的等效结构。 第一个问题是:你真的想要any或者variant<string, int, ...>是一个更好的选择吗?

无论如何,如果您只想包装演员表,您可以轻松做到:

template <typename T>
T json_cast(std::any const& val) 
{
    if constexpr (std::is_enum_v<T>)
    {
        return static_cast<T>(std::any_cast<int>(val));
    }
    else
    {
        return std::any_cast<T>(val);
    }
}

这要求any实际上持有一个int 您也可以尝试使用std::underlying_type_t<T>而不是int ,但枚举的基础类型有一些怪癖,因此如果any持有一个 int,强制转换实际上可能会失败。

C++14版本:

template <typename T>
T json_cast(std::any const& val) 
{    
    using cast_t = typename std::conditional<std::is_enum<T>::value, int, T>::type;
    return static_cast<T>(std::any_cast<cast_t>(val)); 
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM