c++宏定义用法

从上一次写文章到现在已经快2个月了,为啥没更新,因为太忙了!

每天都在为了实现一些麻烦的功能而心急火燎,总是没办法静下心来写文章.所以一直拖延.

对于宏的一些个人用法其实早就想写出来了,碍于时间关系一直拖到现在.

宏在很多人看来就是一个危险的存在.不过在我看来宏真的是一个不可或缺的好东西.之所以被嗤之以鼻,那是有很多人不会正确使用宏,导致项目中宏定义乱飞,各种难以理解的宏嵌套,宏展开编译失败的复杂提示信息.

1.标识当前平台.

因为我的项目需要在windows和linux都要能正常运行,我在windows进行开发,linux上运行.我只是不喜欢在linux上写代码而已,用惯了windows的vs,不太愿意再花太多功夫去习惯别的系统.而且是linux这样的对新手不友好的系统.

先显式定义三个平台标识宏

#define PLATFORM_WINDOWS 0
#define PLATFORM_LINUX 1
#define PLATFORM_ANDROID PLATFORM_LINUX

// 正在运行的平台标识
#ifdef WINDOWS
#define RUN_PLATFORM PLATFORM_WINDOWS
#endif

#ifdef LINUX
#define RUN_PLATFORM PLATFORM_LINUX
#endif

#ifndef RUN_PLATFORM
#define RUN_PLATFORM -1
#error "wrong platform!"
#endif

因为有些头文件只有windows有,有些头文件只有linux才有,所以就会这样写

#if RUN_PLATFORM == PLATFORM_WINDOWS
#include#include#include#include#include#include#include#include#endif
#if RUN_PLATFORM == PLATFORM_LINUX
#include#include#include#include#include#include#include#include#include#include#include#include#include#include#include#include#include#endif

2.统一windows和linux的部分操作

因为很多东西在windows和linux上的类型定义是不同的,但是用法大体是一样的,所以为了方便,就用宏统一起来.

#if RUN_PLATFORM == PLATFORM_WINDOWS
#define MY_THREAD HANDLE
#define MY_SOCKET SOCKET
#define NULL_THREAD NULL
#define THREAD_CALLBACK_DECLEAR(func) static DWORD WINAPI func(LPVOID args)
#define THREAD_CALLBACK(class, func) DWORD WINAPI class##::##func(LPVOID args)
#define CREATE_THREAD(thread, func, args) thread = CreateThread(NULL, 0, func, args, 0, NULL)
#define CLOSE_THREAD(thread)            \
if (thread != NULL_THREAD)      \
{                   \
    TerminateThread(thread, 0); \
    CloseHandle(thread);        \
    thread = NULL_THREAD;       \
}
#define CLOSE_SOCKET(socket) closesocket(socket);
#define CLASS_NAME(T) string(typeid(T).name()).substr(strlen("class "))
#define SPRINTF(buffer, bufferSize, ...) sprintf_s(buffer, bufferSize, __VA_ARGS__)
#elif RUN_PLATFORM == PLATFORM_LINUX
#define MY_THREAD pthread_t
#define MY_SOCKET unsigned int
#define NULL_THREAD 0
#define SOCKADDR_IN sockaddr_in
#define THREAD_CALLBACK_DECLEAR(func) static void* func(void* args)
#define THREAD_CALLBACK(class, func) void* class##::##func(void* args)
#define CREATE_THREAD(thread, func, args) pthread_create(&thread, NULL, func, args)
#define CLOSE_THREAD(thread)            \
if (thread != NULL_THREAD)      \
{                   \
    pthread_cancel(thread);     \
    thread = NULL_THREAD;       \
}
#define CLOSE_SOCKET(socket) close(socket);
#define CLASS_NAME(T) removePreNumber(typeid(T).name())
#define SPRINTF(buffer, bufferSize, ...) sprintf(buffer, __VA_ARGS__)
#endif

其实也就是线程,socket,sprintf的用法统一.

3.一些简单函数的宏,用于提高效率

这类宏其实主要的还是为了效率考虑,一些函数本身就是为了高效运行而存在的,如果再因为调用函数而有一些性能上的损失,真是有点太可惜了.

虽然内联可以达到同样的效果.但是是否真的内联了,还得看编译器实现,并不是定义放到头文件就是内联.我也没有去测试到底怎么样才能保证真的内联.实在是没有多余的时间花在这上面了.所以为了保证没有函数调用的开销,用宏还是最保险的.

// 设置value的指定位置pos的字节的值为byte,并且不影响其他字节
#define SET_BYTE(value, b, pos) value = (value & ~(0x000000FF << (8 * pos))) | (b << (8 * pos))
// 获得value的指定位置pos的字节的值
#define GET_BYTE(value, pos) (value & (0x000000FF > (8 * pos)
#define GET_BIT(value, pos) (((value & (1 > (pos)) & 1)
#define SET_BIT(value, pos, bit) value = value & ~(1 << (pos)) | ((bit) = -MathUtility::MIN_DELTA && value <= MathUtility::MIN_DELTA)
#define IS_FLOAT_EQUAL(value1, value2) (IS_FLOAT_ZERO(value1 - value2))
#define IS_VECTOR3_EQUAL(vec0, vec1) (IS_FLOAT_ZERO(vec0.x - vec1.x) && IS_FLOAT_ZERO(vec0.y - vec1.y) && IS_FLOAT_ZERO(vec0.z - vec1.z))
#define IS_VECTOR2_EQUAL(vec0, vec1) (IS_FLOAT_ZERO(vec0.x - vec1.x) && IS_FLOAT_ZERO(vec0.y - vec1.y))
#define LENGTH_XY(x, y) sqrt(x * x   y * y)
#define LENGTH_XYZ(x, y, z) sqrt(x * x   y * y   z * z)
#define LENGTH_2(vec) sqrt(vec.x * vec.x   vec.y * vec.y)
#define LENGTH_3(vec) sqrt(vec.x * vec.x   vec.y * vec.y   vec.z * vec.z)
#define SQUARED_LENGTH_XY(x, y) (x * x   y * y)
#define SQUARED_LENGTH_XYZ(x, y, z) (x * x   y * y   z * z)
#define SQUARED_LENGTH_2(vec) (vec.x * vec.x   vec.y * vec.y)
#define SQUARED_LENGTH_3(vec) (vec.x * vec.x   vec.y * vec.y   vec.z * vec.z)
#define LENGTH_LESS_3(vec, length) (vec.x * vec.x   vec.y * vec.y   vec.z * vec.z < length * length)
#define LENGTH_GREATER_3(vec, length) (vec.x * vec.x   vec.y * vec.y   vec.z * vec.z > length* length)
#define DOT_3(v0, v1) (v0.x * v1.x   v0.y * v1.y   v0.z * v1.z)
#define DOT_2(v0, v1) (v0.x * v1.x   v0.y * v1.y)
#define CROSS(v0, v1) Vector3(v1.y * v0.z - v0.y * v1.z, v1.x * v0.z - v0.x * v1.z, v1.x * v0.y - v0.x * v1.y)
#define CLAMP(value, minValue, maxValue)\
{\
    if (value > maxValue){value = maxValue;}\
    else if (value < minValue){value = minValue;}\
}
#define CLAMP_MIN(value, minValue) if (value < minValue){value = minValue;}
#define CLAMP_MAX(value, maxValue) if (value > maxValue){value = maxValue;}
#define CLAMP_CYCLE(value, minValue, maxValue, cycle)\
        while (value < minValue)\
        {\
            value  = cycle;\
        }\
        while (value > maxValue)\
        {\
            value -= cycle;\
        }
#define CLAMP_ANGLE(angle, minValue, maxValue, pi) CLAMP_CYCLE(angle, minValue, maxValue, pi * 2.0f)
#define CLAMP_RADIAN_180(radian) CLAMP_ANGLE(radian, -MathUtility::MATH_PI, MathUtility::MATH_PI, MathUtility::MATH_PI)
#define CLAMP_DEGREE_180(degree) CLAMP_ANGLE(degree, -180.0f, 180.0f, 180.0f);
#define CLAMP_RADIAN_360(radian) CLAMP_ANGLE(radian, 0.0f, MathUtility::MATH_PI * 2.0f, MathUtility::MATH_PI)
#define CLAMP_DEGREE_360(degree) CLAMP_ANGLE(degree, 0.0f, 360.0f, 180.0f)
#define TO_DEGREE(radian) (radian * MathUtility::Rad2Deg)
#define TO_RADIAN(degree) (degree * MathUtility::Deg2Rad)
#define SATURATE(value) CLAMP(value, 0.0f, 1.0f)
// 判断value是否在minRange和maxRange之间,并且minRange和maxRange的顺序不固定
#define IS_IN_RANGE(value, minRange, maxRange) (value >= MIN(minRange, maxRange) && value = minRange && value <= maxRange)
#define IS_VECTOR2_IN_RANGE(value, minRange, maxRange) (IS_IN_RANGE(value.x, minRange.x, maxRange.x) && IS_IN_RANGE(value.y, minRange.y, maxRange.y))
#define IS_VECTOR2_IN_RANGE_FIXED(value, minRange, maxRange) (IS_IN_RANGE_FIXED(value.x, minRange.x, maxRange.x) && IS_IN_RANGE_FIXED(value.y, minRange.y, maxRange.y))
#define MIN(value0, value1) (value0 < value1 ? value0 : value1)
#define MAX(value0, value1) (value0 > value1 ? value0 : value1)
#define INVERSE_LERP(a, b, value) ((value - a) / (b - a))
#define LERP_SIMPLE(start, end, t) (start   (end - start) * t)
#define ABS(value) value = value > 0 ? value : -value;
#define SIGN(sign, value)\
        if (value > 0)       {sign = 1;}\
        else if (value < 0)  {sign = -1;}\
        else                {sign = 0;}
#define SWAP(value0, value1)\
        auto& temp = value0;\
        value0 = value1;\
        value1 = temp;
#define CEIL(value) ((int)(value) >= 0.0f && value > (int)(value)) ? (int)(value)   1 : (int)(value)
#define CEIL_2(vec)\
        vec.x = (float)(CEIL(vec.x));\
        vec.y = (float)(CEIL(vec.y));
#define CEIL_3(vec)\
        vec.x = (float)(CEIL(vec.x));\
        vec.y = (float)(CEIL(vec.y));\
        vec.z = (float)(CEIL(vec.z));

4.一些只能使用宏来实现的功能

比如获取当前行号,当然,这是自带的一个宏,可以获取当前行号,类型是整数,只是我这里使用#将其转换为了一个字符串,因为宏展开也是有顺序的,所以这里只能定义两个宏来将__LINE__转换为字符串.

#define STR(t) #t
#define LINE_STR(v) STR(v)
#define _FILE_LINE_ "File : "   string(__FILE__)   ", Line : "   LINE_STR(__LINE__)

5.简单替换

这也许就是宏最简单的用法了,只是单纯的替换文本.

#define FOR_I(count) for (uint i = 0; i < count;   i)
#define FOR_J(count) for (uint j = 0; j < count;   j)
#define FOR_K(count) for (uint k = 0; k < count;   k)
// 基础数据类型转字符串
#define INT_TO_STRING(strBuffer, value)\
char strBuffer[16];\
intToString(strBuffer, 16, value);

#define FLOAT_TO_STRING(strBuffer, value)\
char strBuffer[16];\
floatToString(strBuffer, 16, value);

// 字符串拼接,将str0,str1等字符串拼接后放入charArray中,会覆盖charArray中的内容
#define STRCAT2(charArray, length, str0, str1)\
charArray[0] = '\0';\
StringUtility::strcat_s(charArray, length, str0);\
StringUtility::strcat_s(charArray, length, str1);

#define STRCAT3(charArray, length, str0, str1, str2)\
charArray[0] = '\0';\
StringUtility::strcat_s(charArray, length, str0);\
StringUtility::strcat_s(charArray, length, str1);\
StringUtility::strcat_s(charArray, length, str2);
#define INFO(info) GameLogWrap::logInfo(info, false, true)

比如for循环这种东西写多了也觉得厌烦了,所以就稍微简化了一些

整数转字符串,避免使用堆内存,只使用栈内存,安全而且高效.

6.第4条和第5条的结合,目的还是简化代码

第4条中用到了#用于将宏参数转换为字符串,其他的还有##用于连接文本,__ARGS__用于获取宏参数中的可变参数列表

// 用于遍历stl容器,要在循环结束后添加END
#define FOREACH(iter, stl)\
auto iter = stl.begin();\
auto iter##End = stl.end();\
FOR(stl, ; iter != iter##End;   iter)

// 不带内存合法检测的常规内存申请
#define NORMAL_NEW(className, ptr, ...)         \
NULL;                                           \
ptr = new className(__VA_ARGS__);               \
if(ptr == NULL)                                 \
{                                               \
    ERROR(string("can not alloc memory! ")   "className : "   STR(className));\
}

// 生成静态字符串常量的名字
#define NAME(name) STR_##name
// 声明一个静态字符串常量
#define DECLARE_STRING(name) static const char* NAME(name)
// 定义一个静态字符串常量
#define DEFINE_STRING(name) const char* StringDefine::NAME(name) = STR(name)

// 创建一个消息对象
#define PACKET(classType, packet) classType* packet = mNetServer->createPacket(packet, NAME(classType))

基本也就以上这些了,总之就是用来简化代码,提高运行效率,实现一些常规函数无法实现的功能.总的来说,我还是比较喜欢宏的.

(0)

相关推荐

  • C语言带参数宏定义【编程必备】

    C语言允许宏带有参数.在宏定义中的参数称为形式参数,在宏调用中的参数称为实际参数. #defineM(y)y*y+3*y/*宏定义*/ /* -- */ k=M(5);/*宏调用*/ 在宏调用时,用实 ...

  • C语言宏定义参数的问题

    宏定义是C语言提供的三种预处理功能的其中一种,这三种预处理包括:宏定义.文件包含.条件编译.今天小编给大家有关不带参数宏和带参数宏的一些. 不带参数宏 01 不带参数宏定义又称为宏代换,简称" ...

  • UltraEdit怎么录制宏? UltraEdit快速录制宏的技巧

    UltraEdit中提供了录制宏的功能,该怎么录制宏呢?下面我们就来看看详细的教程. 1.在使用UltraEdit的编辑的功能,以便的使用的宏的录制,解决重复上操作. 2.进行点击UltraEdit的 ...

  • 雷蛇鼠标宏如何设置

    如今游戏众多,但真的玩好一个游戏需要很长的时间,比如英雄联盟,大家一定看过很多秀的英雄,比如盲僧,自己也想玩,可以是手速不行.这就要用到鼠标的宏定义,今天就介绍一下宏定如何设置. 操作方法 01 先打 ...

  • 鼠标宏设置怎么用

    当我们玩一个大型的游戏时,比如英雄联盟,想把它玩得好,可是有时候因为手速不行而导致游戏失败,那么这就要用到鼠标的宏定义了.今天小编就来给小伙伴们介绍一下宏定义的使用方法. 操作方法 01 首先,下载一 ...

  • 爱国者鼠标宏怎么设置

    今天的这篇和大家聊一聊关于爱国者鼠标宏怎么设置的问题,希望能够帮助到有需要的朋友. 操作方法 01 对于鼠标宏的运用首先,我们必须选择有鼠标宏功能的该种鼠标.可以再淘宝上搜索鼠标宏鼠标 即可找到. 0 ...

  • 游戏鼠标宏设置是什么?怎么设置游戏鼠标宏?

    与普通键盘鼠标相比,游戏级键鼠外设拥有着更为丰富的功能,采样率调节.回报率调节.背光.多媒体按键.按键自定义.按键映射.宏编程看起来就十分专业的名词,常常把入门级玩家搞的头昏眼花,至于如何能够使用,更 ...

  • 设置鼠标宏在CF和LOL中一键操作

    操作方法 01 首先,下载一个游戏鼠标的驱动程序,打开安装好的驱动程序,点击宏定义的选项. 02 然后,点击高级设置,选择自定义宏的名称,并增加到宏的列表当中. 03 接着,一直按着键盘上的某个键,然 ...

  • cf鼠标宏设置

    <穿越火线>是一款全球火爆的第一人称射击游戏的网络游戏,也是大众化的FPS网游,玩家体验到的不仅仅体验到了开枪的爽快感,更是来自相互合作及默契带来的战略意义.人物快速移动和瞄准.开枪的打击 ...

  • 绝地求生大逃杀鼠标宏怎么用

    在绝地求生大逃杀的游戏当中,鼠标宏是一个很有用的设定.但是很多玩家不知道怎么用,跟小编一起来看看吧. 操作方法 01 鼠标宏: 宏在计算机中,可以理解成是一串命令的启动按钮. 目前用于游戏行业的宏有鼠 ...