sprintf函数的用法_我在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 <windows.h>
#include <mmsystem.h>
#include <iostream>
#include <io.h>
#include <direct.h>
#include <winsock.h>
#include <tlhelp32.h>
#include <dbghelp.h>
#endif
#if RUN_PLATFORM == PLATFORM_LINUX
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <sys/sysinfo.h>
#include <sys/un.h>
#include <sys/time.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <errno.h>
#include <netdb.h>
#include <stdarg.h>
#include <signal.h>
#include <dirent.h>
#include <pthread.h>
#include <locale.h>
#include <execinfo.h>
#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))) >> (8 * pos)
#define GET_BIT(value, pos) (((value & (1 << (pos))) >> (pos)) & 1)
#define SET_BIT(value, pos, bit) value = value & ~(1 << (pos)) | ((bit) << (pos))
#define GET_HIGHEST_BIT(value) GET_BIT(value, sizeof(value) * 8 - 1)
#define SET_HIGHEST_BIT(value, bit) SET_BIT(value, sizeof(value) * 8 - 1, bit);
#define IS_FLOAT_ZERO(value) (value >= -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 <= MAX(minRange, maxRange))
// 判断value是否在minRange和maxRange之间,并且minRange和maxRange的顺序固定
#define IS_IN_RANGE_FIXED(value, 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
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值