C++之无聊小技巧

通用编程语言的基本特性都相差不远,同时也各有各的玩法。C++特性之多首屈一指,各种其他语言看不到的玩法,当然其实用性也有限。本文简单探讨一些无聊并有趣的技巧,特性或者Idiom。演示这些的目的在娱乐,并不建议在工程中使用。

诊断

C语言本身就有assert(),不过没有输出信息。C++11又加上static_assert(),支持输出信息。
在这之前可以自己实现带输出的诊断:

#define m_assert(condition, message){ \
  if ( !(condition) ) {  \
    print("Assertion failed:", message);  \
    exit(-1); \
  }  \
}  \

或者这样:


assert(condition && message)

事实上我个人还是偏好第二种用法,主要是因为自己定义太麻烦了……其次,输出诊断信息的优势应该是记录日志?发明偷懒技巧的才是神呐。

Policy Based Design

这可以说是《Modern C++ Design》的核心主题。看个简单的例子:

template <typename Case, typename Output>
struct Printer : private Case, private Output {
  void print(string &&msg) {
    Case::convert(msg);
    Output::print(msg);
  }
};
struct LowerCase {
protected:
  void convert(string &msg) {
    transform(msg.begin(), msg.end(), msg.begin(), ::tolower);
  }
};

struct UpperCase {
protected:
  void convert(string &msg) {
    transform(msg.begin(), msg.end(), msg.begin(), ::toupper);
  }
};
struct Cout {
protected:
  void print(string &msg) { cout << msg << endl; }
};
struct Cerr {
protected:
  void print(string &msg) { cerr << msg << endl; }
};

int main() {
  Printer<LowerCase, Cout> printerA;
  printerA.print("Ok, I am good."); 
  //output "ok, i am good." to stdout.
}

实现上玩了些模板技巧,效果相当于延迟继承。它在某种程度保证了多重继承的扁平结构,上述例子理论上可以组合出四种Printer:

 	Printer<LowerCase, Cout>
 	Printer<UpperCase, Cout>
 	Printer<LowerCase, Cerr>
 	Printer<UpperCase, Cerr>

当然这太简化了,现实中的复杂场景,我们能够运用抽象出的正交关系的组合子,搭积木似的实现各种复杂功能。有说法,基于策略的设计本质上就是静态的策略模式。所以实现类似的机制,也可以不用模板,比如使用Go或Java中的接口。但C++模板使我们更加容易设计和维护代码。

Console.log

不知你是否曾羡慕Javascript的console.log()。自C++11之后可以借助Variadic Template实现类似的效果了:

template <typename T> ostream &log(ostream &os, const T &msg) {
  return os << msg << endl;
}
template <typename T, typename... Args>
ostream &log(ostream &os, const T &msg, const Args &... rest) {
  os << msg << ", ";
  return log(os, rest...);
}
int main(){
  log(cout, "123", 123, 'c');
  // output "123, 123, c"
}

这里比较特别的是,需要设置一个递归终结函数。Variadic主要的目的就简化函数声明。换句话说,我们可以对某一功能提供统一的接口,并且不需要太复杂的声明定义。再简单写个例子:

template<typename T, typename... Args>
void destroy(T *t, Args&&... args){
  destroy(t);
  destroy(std::forward<Args>(args)...);
}
template<> inline void destroy<VkInstance>(VkInstance instance){
  destroyInstance(instance);
}
template<> inline void destroy<VkDevice>(VkDevice device){
  destroyDevice(device);
}
template<> inline void destroy<VkBuffer>(VkBuffer buffer){
  destroyBuffer(buffer);
}
int main(){
//.....
  destroy(instance, device, buffer);
}

其实看起来有点累赘…反正本文目的不在实用上。但是,繁琐的细节完全可以留在应用库,比如上面这堆模板。这样对用户可见的就真只有简单的destroy(),甚至还是个变长泛类型参数的,表达力直逼一众动态语言。

随着版本更迭,C++越来越方便,许多技巧已经Deprecated,或许可以拿来炒冷饭?暂时构思好的例子就这三个,有缘再续写下去。

Leave a Reply

Your email address will not be published. Required fields are marked *