首页 > 编程语言 >介绍一个C++奇巧淫技

介绍一个C++奇巧淫技

时间:2023-06-19 10:07:14浏览次数:48  
标签:淫技 JOIN serialize C++ 奇巧 ARG const type define


你能实现这样一个函数吗:

MyType type;
  HisType htype;
  serialize_3(11, type, htype);
  serialize_4(type, htype ,type, htype);
  serialize_4(11, type , htype, htype);

参数类型自由,个数自由,怎么做呢?往下看:


[xiaochu.yh@OB macro]$ cat auto_type.cpp 
/*
 * (C) 1999-2013 Alibaba Inc.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 *
 * Version:  auto_type.cpp,  09/04/2013 08:02:17 PM Yu Huang Exp $
 *
 * Author:
 *   Huang Yu <[email protected]>
 * Description:
 *   auto type match
 *
 */

#include <stdio.h>

void serialize()
{
  return;
}

class HisType
{
  public:
    HisType(const char *i) : value_(i) { }
    ~HisType() { }
    void serialize() const
    {
      printf("HisType: s(%s)\n", value_);
    }
  private:
    const char *value_;
};

class MyType
{
  public:
    MyType(int i) : value_(i) { }
    ~MyType() { }
    void serialize() const
    {
      printf("MyType: f(%d)\n", value_);
    }
  private:
    int value_;
};

void serialize(const int arg0)
{
  printf("int: %d\n", arg0);
}
void serialize(const float arg0)
{
  printf("float: %f\n", arg0);
}

template<typename Arg0>
void serialize(const Arg0 &arg0)
{
  arg0.serialize();
}

template<typename Arg0>
void serialize_1(const Arg0 &arg0)
{
  serialize(arg0);
}

#define JOIN(x,y) JOIN2(x,y)
#define JOIN2(x,y) x##y

#define DECVAL_1 0
#define DECVAL_2 1
#define DECVAL_3 2
#define DECVAL_4 3
#define DEC_VAL(n) DECVAL_##n

// recursively expanding macro
#define ARG_TN0
#define ARG_TN1  typename Arg0
#define ARG_TN2  ARG_TN1, typename Arg1
#define ARG_TN3  ARG_TN2, typename Arg2
#define ARG_TN4  ARG_TN3, typename Arg3

#define ARG_PN0
#define ARG_PN1  const Arg0 & arg0
#define ARG_PN2  ARG_PN1, const Arg1 & arg1
#define ARG_PN3  ARG_PN2, const Arg2 & arg2
#define ARG_PN4  ARG_PN3, const Arg3 & arg3

#define ARG_AN0
#define ARG_AN1  arg0
#define ARG_AN2  ARG_AN1, arg1
#define ARG_AN3  ARG_AN2, arg2
#define ARG_AN4  ARG_AN3, arg3


#define ARG_CN0
#define ARG_CN1  arg0
#define ARG_CN2  arg1
#define ARG_CN3  arg2
#define ARG_CN4  arg3


#define SERIALIZE_DECLARE(NUM_ARG) \
  template<JOIN(ARG_TN, NUM_ARG)> \
void JOIN(serialize_, NUM_ARG)(JOIN(ARG_PN, NUM_ARG))

SERIALIZE_DECLARE(2);
SERIALIZE_DECLARE(3);
SERIALIZE_DECLARE(4);
#define SERIALIZE_DEFINE(NUM_ARG) \
  template<JOIN(ARG_TN, NUM_ARG)> \
void JOIN(serialize_, NUM_ARG)(JOIN(ARG_PN, NUM_ARG)) \
{ \
  JOIN(serialize_, DEC_VAL(NUM_ARG))(JOIN(ARG_AN, DEC_VAL(NUM_ARG))); \
  serialize(JOIN(ARG_CN, NUM_ARG)); \
}


SERIALIZE_DEFINE(2);
SERIALIZE_DEFINE(3);
SERIALIZE_DEFINE(4);

int main()
{
  MyType type(4234);
  HisType htype("home");
  //先来个见面礼, 1是int类型,10.2f是float类型,type是自定义类型
  serialize_4(1,10.2f,3, type);
  printf("==============\n");
  serialize_3(type,11, htype);  // <== 注意下面的参数个数,以及参数顺序,完全自由!
  printf("==============\n");
  serialize_3(11 ,type, htype);
  printf("==============\n");
  serialize_3(htype ,type, htype);
  printf("==============\n");
  return 0;
}



编译运行结果:

[xiaochu.yh@OB macro]$ g++ auto_type.cpp           
[xiaochu.yh@OB]$ ./a.out 
int: 1
float: 10.200000
int: 3
MyType: f(4234)
==============
MyType: f(4234)
int: 11
HisType: s(home)
==============
int: 11
MyType: f(4234)
HisType: s(home)
==============
HisType: s(home)
MyType: f(4234)
HisType: s(home)
==============




该技术是从曲山同学的代码中学习来的,曲山对宏的运用真是炉火纯青!这里最神奇的就是下面一段代码,至今不明:

#define JOIN(x,y) JOIN2(x,y)
#define JOIN2(x,y) x##y



JOIN和JOIN2不是等价的吗?不过还真不是。如果只写JOIN2,在宏展开阶段会有比较诡异的事情发生。不信你试试。但是为什么呢?我也不知道。@曲山,求助啊~~

更全面的代码见OceanBase源码oceanbase/src/common/ob_rpc_stub.h和oceanbase/src/common/ob_rpc_macros.h


=============================================

UPDATE:

这篇帖子发到了内网,得到了@探晴同学指点,加上@元启 同学的解释,基本弄明白了JOIN的机制。

原因的确很简单。 

#define MY_VALUE 2 
 #define JOIN(A,B) A##B
 JOIN(hello, world)的输出结果就是 helloworld,
 JOIN(MY_VALUE, b)的输出结果就是 MY_VALUEb。尽管MY_VALUE是个宏,你期待它展开成2b。 如何成为一个2b呢? 这么做:
 #define JOIN(a, b) JOIN_EXPAND_PARAM(a,b)
 #define JOIN_EXPAND_PARAM(a,b) a##b JOIN(MY_VALUE, b)的展开过程是:
 1. JOIN(MY_VALUE, b)展开成 JOIN_EXPAND_PARAM(2, b)
 2. JOIN_EXPAND_PARAM(2, b) 展开成 2b 这句话:Macro arguments are completely macro-expanded before they are substituted into a macro body, unless they are stringified or pasted with other tokens.

参考: http://gcc.gnu.org/onlinedocs/cpp/Argument-Prescan.html




**=======UPDATE=========**




C++11中引入了可变长参数模板,不需要用上面这么丑的方法了。几句话就搞定,而且不用带上说明参数个数的数字了:

// filename:template.cpp

#include <iostream>
using namespace std;

template<typename T>
void serialize(T value)
{
	cout << value << endl;
}

template <typename Head, typename... Rail>
void serialize(Head head, Rail... rail)
{   
	cout << head << ",";
	serialize(rail...);
}

int main(int argc, char *argv[])
{
	serialize(1);                  // 输出:1
	serialize(1, "hello");         // 输出:1,Hello
	serialize(1, "hello", 'H');    // 输出:1,Hello,H
	return 0;
}





编译的时候需要试用-std=c++0x或者 -std=gnu++0x


g++ -std=c++0x template.cpp  -o template

标签:淫技,JOIN,serialize,C++,奇巧,ARG,const,type,define
From: https://blog.51cto.com/maray/6510867

相关文章

  • 《C++》多态
    多态多态分为两种:静态多态:函数重载和运算符重载属于静态多态,复用函数名动态多态:派生类和虚函数实现运行时多态静态多态函数地址早绑定--编译阶段确定函数地址动态多态函数地址晚绑定--运行阶段确定函数地址virtual  //修饰虚函数,使之变为动态多态特点代码结构清晰可读性强......
  • C++常用数据结构
    数据结构1.线性表由n个具有相同性质的数据元素1.1顺序表(数组)定义:用一组地址连续的存储单元依次存储线性表中每个数据元素特点:逻辑关系相邻的两个元素在物理位置上也相邻#c++实现template<typenameT>classsqlist{public:sqlist(intmaxsize=10):Maxsize(......
  • 现代C++学习指南-类型系统
    在前一篇,我们提供了一个方向性的指南,但是学什么,怎么学却没有详细展开。本篇将在前文的基础上,着重介绍下怎样学习C++的类型系统。写在前面在进入类型系统之前,我们应该先达成一项共识——尽可能使用C++的现代语法。众所周知,出于兼容性的考虑,C++中很多语法都是合法的。但是随着新......
  • #yyds干货盘点#C++命名空间
    命名空间命名空间是C++语言的新特性,它能够解决命名冲突问题。例如,小明定义了一个函数swap(),C++标准程序库中也存在一个swap()函数。此时,为了区分调用的是哪个swap()函数,可以通过命名空间进行标识。C++中的命名空间包括两种,具体介绍如下。usingnamespacestd;1.标准命名空间std是C+......
  • C++17特性
    构造函数模板推导在C++17前构造一个模板类对象需要指明类型:pair<int,double>p(1,2.2);//beforec++17C++17就不需要特殊指定,直接可以推导出类型,代码如下:pairp(1,2.2);//c++17自动推导vectorv={1,2,3};//c++17结构化绑定1.获取值//绑定tuplestd::tupl......
  • C++练习题
    多态判断Q1:虚函数可以是内联的?A1:错误。内联是编译时刻决定的,虚函数是运行时刻动态决定的,所以虚函数不能是内联函数。虚函数前加上inline不会报错,但是会被忽略。Q2:一个类内部,可以同时声明staticvoidfun()和virutalvoidfun()两个函数?A2:错误。虽然静态函数......
  • C++面试八股文:什么是RAII?
    C++面试八股文:什么是RAII?某日二师兄参加XXX科技公司的C++工程师开发岗位第13面:面试官:什么是RAII?二师兄:RAII是ResourceAcquisitionIsInitialization的缩写。翻译成中文是资源获取即初始化。面试官:RAII有什么特点和优势?二师兄:主要的特点是,在对象初始化时获取资源,在对......
  • C++基础知识总结
    2023/6/18本篇章记录学习过程C++的基础概念和代码测试实现,还有很多需要补充。一是还不清楚,二是还没有学到。打算学习过程中后面再做补充。先看完《C++primer》书之后再慢慢来添加补充1.函数重载一个函数名可以实现多个功能,这取决于函数参数不同来实现判断对应的功能,与返回......
  • C++面试八股文:std::string是如何实现的?
    某日二师兄参加XXX科技公司的C++工程师开发岗位第18面:面试官:std::string用过吧?二师兄:当然用过(废话,C++程序员就没有没用过std::string的)。面试官:std::string("hello")+"world"、"hello"+std::string("world")和std::string("hello")+std::string("world")的......
  • C++面试八股文:std::string是如何实现的?
    某日二师兄参加XXX科技公司的C++工程师开发岗位第18面:面试官:std::string用过吧?二师兄:当然用过(废话,C++程序员就没有没用过std::string的)。面试官:std::string("hello")+"world"、"hello"+std::string("world")和std::string("hello")+std::string("world")的......