很酷的应用:
(1) 如何获取可变参数名
代码例子:
#define _test(...) (test_t(#__VA_ARGS__, __VA_ARGS__))
template<typename... Args>
void test_t(const char* names, Args... args)
{
std::cout << names << "\n";
}
__VA_ARGS__
是 C/C++ 宏定义中的一个特殊标识符,用于表示 可变参数宏(Variadic Macros)中的参数包。它允许宏接受任意数量的参数。
详细说明
-
可变参数宏:
-
在宏定义中,使用
...
表示可变参数。 -
使用
__VA_ARGS__
来引用这些可变参数。
-
-
#__VA_ARGS__
:-
#
是字符串化运算符,将宏参数转换为字符串。 -
#__VA_ARGS__
将可变参数包中的所有参数转换为一个字符串。
-
#define _test(...) (test_t(#__VA_ARGS__, __VA_ARGS__))
这条语句定义了一个宏 _test
,它接受可变数量的参数(通过 ...
表示),并将这些参数传递给一个函数 test_t
:
1. 宏定义的基本结构
-
_test
是宏的名称。 -
(...)
表示宏可以接受任意数量的参数。 -
test_t(#__VA_ARGS__, __VA_ARGS__)
是宏的展开内容。
2. __VA_ARGS__
的作用
-
__VA_ARGS__
是 C/C++ 中的特殊标识符,用于表示可变参数宏中的参数包。 -
它允许宏接受任意数量的参数。
3. #__VA_ARGS__
的作用
-
#
是字符串化运算符,将宏参数转换为字符串。 -
#__VA_ARGS__
将可变参数包中的所有参数转换为一个字符串。
4. 宏的展开逻辑
假设调用 _test(a, b, c)
,宏会展开为:
(test_t("a, b, c", a, b, c))
-
#__VA_ARGS__
将a, b, c
转换为字符串"a, b, c"
。 -
__VA_ARGS__
展开为a, b, c
。
5. test_t
函数的作用
test_t
是一个函数,它至少接受两个参数:
-
第一个参数是字符串
"a, b, c"
,表示变量名。 -
后续参数是变量值
a, b, c
。
test_t
的具体实现需要根据需求编写。例如,它可以用于打印变量名和变量值,或者进行其他处理。
下面看一下例子:
#define _test(...) (test_t(#__VA_ARGS__, __VA_ARGS__))
template<typename... Args>
void test_t(const char* names, Args... args)
{
std::cout << names << "\n";
}
int main(int argc, char* argv[])
{
QApplication a(argc, argv); //注意,这里是QApplication
ga.setStdLocaleForUTF8();
int i = 5;
double c = 3.14;
std::string s = "abcd";
_test(i, c,s);
return a.exec();
}
运行结果:
请注意变量 i 和 c 中间是有一个空格的,这跟你的书写格式有关:
例如:
(2)如何展开参数包。
#define _test(...) (test_t(#__VA_ARGS__, __VA_ARGS__))
template<typename... Args>
void test_t(const char* names, Args... args)
{
auto arrNames = _string(names).split(','); //拆分变量列表
for (auto s : arrNames) {
std::wcout << _t("s=") << s << _t("\n");
}
}
int main(int argc, char* argv[])
{
QApplication a(argc, argv); //注意,这里是QApplication
ga.setStdLocaleForUTF8();
int i = 5;
double c = 3.14;
std::string s = "abcd";
_test(i,c,s);
return a.exec();
}
输出结果:
(3)如何获取变量的值,这里只介绍C++17及其以上的写法。
#define _test(...) (test_t(#__VA_ARGS__, __VA_ARGS__))
template<typename... Args>
void test_t(const char* names, Args... args)
{
auto arrNames = _string(names).split(','); //拆分变量列表
int i = 0;
// 使用折叠表达式展开参数包,只支持C++17及其以上
//-------------------------------------------------------------------------
(
(
std::wcout << arrNames[i],
std::cout << "=",
std::cout << args << "\n",
++i //下一个参数
),
...);
}
int main(int argc, char* argv[])
{
QApplication a(argc, argv); //注意,这里是QApplication
ga.setStdLocaleForUTF8();
int i = 5;
double c = 3.14;
std::string s = "abcd";
_test(i,c,s);
return a.exec();
}
运行结果:
(4)应用例子:
//在控制台打印出n个变量名和变量值
#define _pns(...) (_cout << _generateString(#__VA_ARGS__, __VA_ARGS__))
//在窗口中显示n个变量名和变量值
#define _pnw(...) (qt.showText(_generateString(#__VA_ARGS__, __VA_ARGS__)))
int main(int argc, char* argv[])
{
QApplication a(argc, argv); //注意,这里是QApplication
ga.setStdLocaleForUTF8();
QWidget w;
w.resize(500, 800);
int i = 5;
double d = 3.14;
std::string s = "abcd";
_Color c;
_Font f;
_pns(i, d, s, w.geometry(), c, f);
_pnw(i, d, s, w.geometry(), c, f);
return a.exec();
}
结果:
下面是代码:
#if _c17_
/*-------------------------------------------- - 源程序(由DeepSeek提供)
// 辅助函数,生成变量名和值的字符串
template<typename... Args>
std::string _generateString(const char* names, Args... args) {
std::string result;
std::ostringstream oss;
std::istringstream iss(names);
std::string name;
((oss << (iss >> name ? name : "") << " = " << args << "<br>"), ...);
return oss.str();
}
*/
/// <summary>
/// 参数名 = 参数值
/// </summary>
/// <typeparam name="...Args"></typeparam>
/// <param name="names"></param>
/// <param name="...args"></param>
/// <returns></returns>
/// 创建时间:2025-03-07 最后一次修改时间:2025-03-07 (已测试)
template<typename... Args>
_string _generateString(const char* names, Args... args) {
_string result;
using namespace lf;
auto arrNames = _string(names).split(','); //拆分变量列表
int i = 0;
const char* namePtr = names;
//std::cout << "names=" << names << "\n";
// 使用折叠表达式展开参数包
//((result += std::string(namePtr) + " = " + std::to_string(args) + "<br>", namePtr = nullptr), ...);
// 使用折叠表达式展开参数包,只支持C++17及其以上
//-------------------------------------------------------------------------
(
(
// names = i, d, f, w.geometry(),用逗号和一个空格分隔,是固定的。
//result.append(_string(typeid(args).name())),
//result.append(_t("<br>")),
//result.append(_t("----------")),
//result.append(_t("<br>")),
//name = _string(namePtr).left(_t(", ")),
result.add(arrNames[i].trim()),
result.append(_t("=")),
result.append(_tostr(args)),
//result.append(_t("<br>")), //如果是纯文本 <br> 替代成 \n
result.append(_t("\n")),
++i //下一个参数
),
...);
//-------------------------------------------------------------------------
return result;
}
#else
/*---------------------------------------------源程序(由DeepSeek提供)
// 递归终止条件
std::string _generateStringHelper(const char* namePtr) {
return "";
}
// 递归展开参数包
template<typename T, typename... Args>
std::string _generateStringHelper(const char* namePtr, T arg, Args... args) {
std::string result;
// 提取变量名
std::string name;
while (*namePtr && *namePtr != ',') {
name += *namePtr++;
}
if (*namePtr == ',') namePtr++; // 跳过逗号
while (*namePtr == ' ') namePtr++; // 跳过空格
// 拼接变量名和值
result += name + " = " + std::to_string(arg) + "<br>";
// 递归处理剩余参数
result += _generateStringHelper(namePtr, args...);
return result;
}
template<typename... Args>
std::string _generateString(const char* names, Args... args) {
return _generateStringHelper(names, args...);
}
*/
// 递归终止条件
_string _generateStringHelper(const char* namePtr) {
return _t("");
}
// 递归展开参数包
template<typename T, typename... Args>
_string _generateStringHelper(const char* namePtr, T arg, Args... args) {
_string result;
_pn(namePtr);
// 提取变量名
_string name;
while (*namePtr && *namePtr != ',') {
name.append(*namePtr++);
}
if (*namePtr == ',') namePtr++; // 跳过逗号
while (*namePtr == ' ') namePtr++; // 跳过空格
// 拼接变量名和值
result += name + _t(" = ") + _tostr(arg) + _t("<br>");
// 递归处理剩余参数
result += _generateStringHelper(namePtr, args...);
return result;
}
template<typename... Args>
_string _generateString(const char* names, Args... args) {
return _generateStringHelper(names, args...);
}
#endif
这里有个关键函数,_tostr上次已介绍过:
_tostr
/// <summary>
///
/// </summary>
/// <param name="pt"></param>
/// <param name="sTypeName"></param>
/// <returns></returns>
/// 创建时间:2025-02-16 最后一次修改时间:2025-02-16
_string _tostr(void* pt, const char* sTypeName);
/// <summary>
///
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="t"></param>
/// <returns></returns>
/// 创建时间:2025-02-16 最后一次修改时间:2025-02-16
template<class T>
inline _string _tostr(const T& t) {
return _tostr((void*)(&t), typeid(t).name());
}
_string _tostr(void* pt, const char* sTypeName)
{
_StrA sType = sTypeName;
_string sResult(_t(""), 100);
if (sType == typeid(char*).name() || sType == typeid(const char*).name()) {
char** ppC = ((char**)(pt)); //指向指针的指针
sResult.add(_string::qt_fromUtf8(*ppC));
}
else if (sType == typeid(wchar_t*).name() || sType == typeid(const wchar_t*).name()) {
wchar_t** ppC = ((wchar_t**)(pt)); //指向指针的指针
sResult.add(*ppC);
}
#ifdef _QT_ //---------------------------------------------------------_QT_
else if (sType == typeid(QStringList).name()) {
QStringList* data = (QStringList*)pt;
if (data->size() == 0)
sResult.add(_t("{}"));
else {
sResult.add(_t("{"));
for (int i = 0; i < data->size() - 1; ++i) {
sResult.add(_t("\""));
sResult.add(data->at(i).toStdWString());
sResult.add(_t("\","));
}
sResult.add(_t("\""));
sResult.add(data->last().toStdWString());
sResult.add(_t("\""));
sResult.add(_t("}"));
}
}
else if (sType == typeid(QString).name()) {
sResult.add(_t("\""));
sResult.add((*((QString*)(pt))).toStdWString());
sResult.add(_t("\""));
}
else if (sType == typeid(QDateTime).name()) {
return QtDateTimeToString((QDateTime*)pt);
}
else if (sType == typeid(QFont).name()) {
QFont* pf = (QFont*)(pt);
sResult.add(pf->toString().toStdWString());
}
else if (sType == typeid(QByteArray).name()) {
QByteArray* pb = (QByteArray*)(pt);
sResult.add(_string::fromOnlyData(pb->data(), pb->size()));
}
else if (sType == typeid(QColor).name()) {
QColor* pc = (QColor*)(pt);
//sResult.add(_string::fromOnlyData(pb->data(), pb->size()));
/*
alpha:透明度。
red:红色分量。
green:绿色分量。
blue:蓝色分量。
pad:填充字段。
*/
_Color c;
c._a = pc->alpha();
c._r = pc->red();
c._g = pc->green();
c._b = pc->blue();
sResult.add(c.toString());
}
else if (sType == typeid(QSize).name()) {
QSize* ps = (QSize*)(pt);
sResult.add(_t("("));
sResult.add(_Math::intToStrForBaseN(ps->width()));
sResult.add(_t(","));
sResult.add(_Math::intToStrForBaseN(ps->height()));
sResult.add(_t(")"));
}
else if (sType == typeid(QRect).name()){
QRect* pr = (QRect*)(pt);
sResult.add(_t("("));
sResult.add(_Math::intToStrA(pr->left()));
sResult.add(_t(","));
sResult.add(_Math::intToStrA(pr->top()));
sResult.add(_t(","));
sResult.add(_Math::intToStrA(pr->bottom()));
sResult.add(_t(","));
sResult.add(_Math::intToStrA(pr->right()));
sResult.add(_t(")"));
}
#endif // ---------------------------------------------------------_lf_
else if (sType == typeid(_StrListW).name()) {
_StrListW* data = (_StrListW*)pt;
if (data->size() == 0)
sResult.add(_t("{}"));
else {
sResult.add(_t("{"));
for (int i = 0; i < data->size() - 1; ++i) {
sResult.add(_t("\""));
sResult.add(data->at(i).qt_toStdWString());
sResult.add(_t("\","));
}
sResult.add(_t("\""));
sResult.add(data->last()->data.qt_toStdWString());
sResult.add(_t("\""));
sResult.add(_t("}"));
}
}
else if (sType == typeid(_Color).name()) {
_Color* pc = ((_Color*)(pt));
sResult.add(pc->toString());
}
else if (sType == typeid(_Font).name()) {
_Font* pf = ((_Font*)(pt));
sResult.add(pf->toString());
}
else if (sType == typeid(_GuiTreeNodeEncryptionType).name()) {
_GuiTreeNodeEncryptionType* ptnt = ((_GuiTreeNodeEncryptionType*)(pt));
if (*ptnt == _GuiTreeNodeEncryptionType::noEncryption) {
sResult.add(_t("_GuiTreeNodeEncryptionType::noEncryption"));
}
else if (*ptnt == _GuiTreeNodeEncryptionType::simpleEncryption) {
sResult.add(_t("_GuiTreeNodeEncryptionType::simpleEncryption"));
}
else if (*ptnt == _GuiTreeNodeEncryptionType::aesEncryption) {
sResult.add(_t("_GuiTreeNodeEncryptionType::aesEncryption"));
}
else if (*ptnt == _GuiTreeNodeEncryptionType::desEncryption) {
sResult.add(_t("_GuiTreeNodeEncryptionType::desEncryption"));
}
}
else if (sType == typeid(int).name()) {
int *pNum = ((int*)(pt));
sResult.add(_Math::intToStr(*pNum));
}
else if (sType == typeid(float).name()) {
float* pNum = ((float*)(pt));
sResult.add(_Math::doubleToStr(*pNum));
}
else if (sType == typeid(double).name()) {
double *pNum = ((double*)(pt));
sResult.add(_Math::doubleToStr(*pNum));
}
else if (sType == typeid(__int64).name()) {
__int64 *pNum = ((__int64*)(pt));
sResult.add(_Math::intToStr(*pNum));
}
else if (sType == typeid(_byte).name()) {
_byte *pNum = ((_byte*)(pt));
sResult.add(_Math::intToStr(*pNum));
}
else if (sType == typeid(_int8).name()) {
_int8 *pNum = ((_int8*)(pt));
sResult.add(_Math::intToStr(*pNum));
}
else if (sType == typeid(_int16).name()) {
_int16 *pNum = ((_int16*)(pt));
sResult.add(_Math::intToStr(*pNum));
}
else if (sType == typeid(_int32).name()) {
_int16 *pNum = ((_int16*)(pt));
sResult.add(_Math::intToStr(*pNum));
}
else if (sType == typeid(_int64).name()) {
_int64 *pNum = ((_int64*)(pt));
sResult.add(_Math::intToStr(*pNum));
}
else if (sType == typeid(_uint8).name()) {
_uint8 *pNum = ((_uint8*)(pt));
sResult.add(_Math::intToStr(*pNum));
}
else if (sType == typeid(_uint16).name()) {
_uint16 *pNum = ((_uint16*)(pt));
sResult.add(_Math::intToStr(*pNum));
}
else if (sType == typeid(_uint32).name()) {
_uint32 *pNum = ((_uint32*)(pt));
sResult.add(_Math::intToStr(*pNum));
}
else if (sType == typeid(_uint64).name()) {
_uint64* pNum = ((_uint64*)(pt));
sResult.add(std::to_wstring(*pNum));
}
else if (sType == typeid(std::string).name()) {
std::string* ps = ((std::string*)(pt));
sResult.add(_string::qt_fromStdString(*ps));
}
else if (sType == typeid(std::wstring).name()) {
std::wstring* ps = ((std::wstring*)(pt));
sResult.add(_string::qt_fromStdWString(*ps));
}
else if (sType == typeid(_string).name()) {
_string* ps = ((_string*)(pt));
sResult.add(*ps);
}
else if (sType.indexOf("char const [") != -1) {//char const [9]
const char* ps = (char*)(pt);
sResult.add(_string::qt_fromUtf8(ps));
}
else if (sType.indexOf("wchar_t const [") != -1) {//char const [9]
const wchar_t* ps = (wchar_t*)(pt);
sResult.add(ps);
}
else if (sType == typeid(bool).name()) {
bool* pb = (bool*)(pt);
if (*pb)
sResult.add(_t("true"));
else
sResult.add(_t("false"));
}
else if (sType.indexOf("lf::_DList") != -1) //class lf::_DList<class lf::_StrW>
{
_Object* po = (_Object*)(pt);
return po->toString();
}
else if (sType == typeid(_Object).name())
{
_Object* po = (_Object*)(pt);
return po->toString();
}
else {
sResult.add(sType);
}
return sResult;
}