简体   繁体   English

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

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

I wrote a generic class to provide an easy JSON-based init to any class.我写了一个通用的 class 来为任何 class 提供一个简单的基于 JSON 的初始化。 It was working like a charm until I want to apply it to a class that contain an enum.在我想将它应用到包含枚举的 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; 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; where the key are the field name and the boost::any contain string, int, double and even array of number.其中键是字段名称, boost::any 包含字符串、整数、双精度甚至数字数组。

My derived class just need to implement an Update function like in the following example:我派生的 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";

The UPDATE_VALUE(testJsonUpdate, _test_int); UPDATE_VALUE(testJsonUpdate, _test_int); is a MACRO and it's expansion use the following code.是一个宏,它的扩展使用以下代码。 The question is what can I do when my derived class has an emun member.问题是当我的派生 class 有一个 emun 成员时我该怎么办。 In that case, the boost::any value is an integer, and for now the call to the explicit operator T() with T = "MYCLASS::enum type" trigger an exception because of the boost::any_cast from int to my enum type !在这种情况下,boost::any 值是 integer,现在使用 T = "MYCLASS::enum type" 调用explicit operator T()会触发异常,因为 boost::any_cast 从 int 到 my枚举类型!

is there a way to write a cast operator that boost::any cast use to solve that issue?有没有办法编写一个转换运算符来提升::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); \
}

Full example to demonstrate the issue, tested on https://www.onlinegdb.com/online_c++_compiler with C++17 (to have the std::any support)演示该问题的完整示例,在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;
}

I'm not familiar with boost so I'll use the standard equivalent constructs.我对 boost 不熟悉,所以我将使用标准的等效结构。 First question is: do you really want any or is variant<string, int, ...> a better choice?第一个问题是:你真的想要any或者variant<string, int, ...>是一个更好的选择吗?

Anyway, if you just want to wrap the cast you can do that easily:无论如何,如果您只想包装演员表,您可以轻松做到:

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

This requires that the any actually holds an int .这要求any实际上持有一个int You could also try std::underlying_type_t<T> instead of int but enums have some quirks with their underlying types so the cast could actually fail if the any holds an int.您也可以尝试使用std::underlying_type_t<T>而不是int ,但枚举的基础类型有一些怪癖,因此如果any持有一个 int,强制转换实际上可能会失败。

C++14 version: 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