函数名称的模板替换

Template substitution for function names

本文关键字:替换 函数      更新时间:2023-10-16

我正在使用第三方C++库,该库有一个 json 解析类,该类具有不同的功能,如下所示:

GetInt  
GetBool
GetString
GetDouble 

我想编写一个可以访问此类的实用程序函数。我在想这样的事情:

class <template T>
class MyClass {
static T getValue(ThirdPartyClass someObj, const string &key) {
   if(someObj[key].IsDouble())
       return someObj[key].GetDouble();
   else if (someObj[key].IsString())
       return someObj[key].GetString();
   // ... (for other types)
} 
}

此类的调用方将保存正确的返回类型。

然而,这是丑陋的。有什么方法(例如使用宏替换)可以避免 if 条件吗?第三方类具有 IsXXTypeXX 和相应的 GetXXTypeXX 函数(其中 XXTypeXX 是 Int、Double、String 或 Bool)。

当我调用函数时,我知道返回类型,例如:

int i = getValue(someObj, "intKey");
string s = getValue(someObj, "strKey");

所以我根本不需要 if 条件。理想情况下,我会期待拥有这样的东西我将能够做到这一点:

int i = MyClass<int>::getValue(someObj, "intKey"); 
string s = MyClass<string>::getValue(someObj, "strKey"); 

为什么不编写一堆静态 Get 函数(GetInt/GetDouble...)来验证输入,返回适当的类型结果,如果不是该类型,则引发异常?

从技术上讲,您可以实现您在那里概述的公共接口,但这将涉及非常丑陋的模板专业化。如果你只有一堆静态函数,可能会更好。以下是模板专用化的外观:

template <typename T> class MyClass { 
    static T getValue(ThirdPartyClass someObj, const string &key) {
       // handle types that you didn't specialize for
    } 
};
template <> class MyClass <string> { 
    static string getValue(ThirdPartyClass someObj, const string &key) {
       return someObj[key].GetString();
    } 
};
template <> class MyClass <int> { 
    static int getValue(ThirdPartyClass someObj, const string &key) {
       return someObj[key].GetInt();
    } 
};
//..

软件工程的骨架关键:添加一个中间层。

#include <string>
#include <cassert>
using std::string;
class Proxy {
public:
  enum Type {
    Int,
    Bool,
    String,
    Double
  };
  Type type;
  int i;
  bool b;
  string s;
  double d;
  operator int() const {
    assert(type == Int);
    return i;
  }
  operator bool() const {
    assert(type == Bool);
    return b;
  }
  operator string() const {
    assert(type == String);
    return s;
  }
  operator double() const {
    assert(type == Double);
    return d;
  }
  Proxy(int i) : type(Int), i(i) {}
  Proxy(bool b) : type(Bool), b(b) {}
  Proxy(string s) : type(String), s(s) {}
  Proxy(double d) : type(Double), d(d) {}
}; // class Proxy
Proxy getValue(ThirdPartyClass someObj, const string &key) {
  if (someObj[key].IsDouble())
    return someObj[key].GetDouble();
  else if (someObj[key].IsString())
    return someObj[key].GetString();
   //... (for other types)
}
int main() {
  int i = getValue(someObj, "intKey"); // if type does not match, a exception will be thrown.
  string s = getValue(someObj, "strKey");
}

您显示的代码无法编译。你不能在同一个函数中返回一个double、一个string和一个int。你必须做什么,它专门针对每个返回类型,然后只调用该类型的函数:

template <>
class MyClass<int> getValue(ThirdPartyClass someObj, const string& key) {
    if(someOjb[key].IsInt()) return someObj[key].GetInt();
    else { /* Maybe throw an exception */ }
};

并对每种类型重复此操作。

现在,你可能会想,"这很愚蠢,为什么我必须专门研究每种类型?这是因为您的 JSON 库正在使用类型擦除,因此您必须在运行时检查类型。保存工作的唯一方法是库提供模板化get

如果需要,可以创建一个宏来标记这些实例化。它将利用预处理器的#(字符串化)和##(串联)功能。把它们写出来可能会更清楚。