考虑到C ++中没有反射,我如何才能使其更加健壮?

时间:2016-09-22 22:46:16

标签: c++ c++11

我有以下课程:

template <typename T>
T get_value(std::string name, T defaultValue)
{
  // read values from config file and assign to var; if not found use defaultValue
  return defaultValue;
}

class A {
public:
  A()
    : myvar1_(get_value("myvar1", 0))
    , myvar2_(get_value("myvar2", 1.5))
  {
  }

  int myvar1_;
  double myvar2_;

  std::string asString() const {
    std::stringstream str;
    str << "myvar1 = " << myvar1_
        << ", myvar2 = " << myvar2_;
    return str.str();
  }
private:
  // other things exist here, unrelated to myvar1/2
};

其中get_value是一个从某个配置文件中读取变量值的函数,如果未找到则使用默认值。我还有myvar1_myvar2_作为公共成员变量,因为它们可以直接访问,我想保留该功能,并且不想添加潜在的函数跃点。

现在,您可以看到我在很多不同的地方输入了myvar1myvar1_,我想让它更加强大,以便我可以在某处以某种方式键入myvar1_, "myvar1", 0 一次(而不是键入myvar1_ 3次,"myvar1"两次),并自动获取上述函数和填充值。我有很多变量并且它们经常被添加或删除,有时我忘记初始化它们,或者错误地输入set_value中的字符串名称,或者忘记向asString添加新变量。 / p>

这可能吗?我很感激任何提示。

2 个答案:

答案 0 :(得分:3)

选项1:从定义生成代码的DSL。

选项2:在配置查找中使用类外观,可能通过访问您的配置类来支持,并与C ++ 11的成员初始化相结合。这将减少你必须重复一个变量的次数,如果你想要这个变量,那么可以用一点宏来隐藏它。

#include <iostream>

template<typename T>
class DV {
    T value_;
public:
    DV(const char* name, const T& defaultValue)
    {
        // read values from config file and assign to var; if not found use defaultValue
        value_ = defaultValue;
    }

    operator const T& () const { return value_; }
};

using DVInt = DV<int>;
using DVStr = DV<const char*>;

struct A {
    int i_ = DVInt("i", 42);
    const char* str_ = DVStr("str", "hello");

    A() = default;
};

int main() {
    A a;
    std::cout << a.i_ << ", " << a.str_ << "\n";
}

演示:http://ideone.com/RAyKwI

- 编辑 -

使用宏减少一个实例。

而不是using语句:

#include <iostream>

template<typename T>
class DV {
    T value_;
public:
    DV(const char* name, const T& defaultValue)
    {
        // read values from config file and assign to var; if not found use defaultValue
        value_ = defaultValue;
    }

    operator const T& () const { return value_; }
};

#define CONCAT(x, y) x ## y
#define DVDecl(T, name, def) T CONCAT(name, _) = DV<T>(#name, def)
#define DVInt(name, def) DVDecl(int, name, def)
#define DVCStr(name, def) DVDecl(const char*, name, def)

struct A {
    DVInt(myvar1, 42);
    DVCStr(myvar2, "hello");

    A() = default;
};

int main() {
    A a;
    std::cout << a.myvar1_ << ", " << a.myvar2_ << "\n";
}

http://ideone.com/JmgfH9

但这并不能消除您手动将它们添加到asString的需要。

答案 1 :(得分:0)

我最终得到了一个宏观解决方案,大量借用了不同的SO答案:

#define EVAL0(...) __VA_ARGS__
#define EVAL1(...) EVAL0 (EVAL0 (EVAL0 (__VA_ARGS__)))
#define EVAL2(...) EVAL1 (EVAL1 (EVAL1 (__VA_ARGS__)))
#define EVAL3(...) EVAL2 (EVAL2 (EVAL2 (__VA_ARGS__)))
#define EVAL4(...) EVAL3 (EVAL3 (EVAL3 (__VA_ARGS__)))
#define EVAL(...)  EVAL4 (EVAL4 (EVAL4 (__VA_ARGS__)))

#define MAP_END(...)
#define MAP_OUT

#define MAP_GET_END0() 0, MAP_END
#define MAP_GET_END1(...) 0

#define GET_MACRO(_0, _1, _2, _3, _4, NAME, ...) NAME
#define MAP_GET_END(...) GET_MACRO(_0, ##__VA_ARGS__, MAP_GET_END1, MAP_GET_END1, MAP_GET_END1, MAP_GET_END1, MAP_GET_END0)(__VA_ARGS__)

#define MAP_NEXT0(item, next, ...) next MAP_OUT
#define MAP_NEXT1(item, next) MAP_NEXT0 (item, next, 0)
#define MAP_NEXT(item, next)  MAP_NEXT1 (MAP_GET_END item, next)

#define MAP0(f, x, peek, ...) f(x) MAP_NEXT (peek, MAP1) (f, peek, __VA_ARGS__)
#define MAP1(f, x, peek, ...) f(x) MAP_NEXT (peek, MAP0) (f, peek, __VA_ARGS__)

#define MAP(f, ...) EVAL (MAP1 (f, __VA_ARGS__, (), 0))


#define DEFINE_VARS_T(TYPE, NAME, DEFAULT_VALUE) \
  TYPE NAME##_;
#define DEFINE_VARS(TUPLE) DEFINE_VARS_T TUPLE

#define CONSTRUCT_VARS_T(TYPE, NAME, DEFAULT_VALUE) \
  NAME##_ = get_value(#NAME, DEFAULT_VALUE);
#define CONSTRUCT_VARS(TUPLE) CONSTRUCT_VARS_T TUPLE

#define PRINT_VARS_T(TYPE, NAME, DEFAULT_VALUE) \
  << #NAME << " = " << NAME##_ << ", "
#define PRINT_VARS(TUPLE) PRINT_VARS_T TUPLE

#define CONFIG_VARS(...) \
  MAP(DEFINE_VARS, __VA_ARGS__) \
  A() { \
    MAP(CONSTRUCT_VARS, __VA_ARGS__) \
  } \
  std::string asString() const { \
    std::stringstream str; \
    str MAP(PRINT_VARS, __VA_ARGS__); \
    return str.str(); \
  }

template <typename T>
T get_value(std::string name, T defaultValue)
{
  // read values from config file and assign to var
  return defaultValue;
}

class A {
public:
  CONFIG_VARS
  (
    (int, myvar1, 0),
    (double, myvar2, 1.5),
    (std::string, myvar3, "what")
  )

private:
  // other things exist here, unrelated to myvar1/2
};