0%

Extract struct field name in C++20

趁出去玩之前發一下文章,在C++ Reflection還沒正式定案之前,總有自救會想盡辦法來解決這些問題

Pre C++20

在C++20之前,最簡單的方法就是用Macro了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include <iostream>

#define PRINT_FIELD_NAME(struct_type, field) \
std::cout << #field << std::endl;

template <typename T>
void printFieldNames(const T& obj) {
// Do nothing in this template; specialize it for each struct
}

#define SPECIALIZE_PRINT_FIELD_NAMES(struct_type, ...) \
template <> \
void printFieldNames<struct_type>(const struct_type& obj) { \
__VA_ARGS__ \
}

// Define your struct
struct MyStruct {
int field1;
double field2;
char field3;
};

// Specialize the printFieldNames template for your struct
SPECIALIZE_PRINT_FIELD_NAMES(MyStruct,
PRINT_FIELD_NAME(MyStruct, field1)
PRINT_FIELD_NAME(MyStruct, field2)
PRINT_FIELD_NAME(MyStruct, field3)
)

int main() {
MyStruct myObj;
printFieldNames(myObj);

return 0;
}

Macro的方案都差不多,不過問題是出在Macro,由於在Preprocessor階段,得不到C++2的語意,所以會遇上以下問題

  • 難維護
  • 新增新功能困難

接著就到了C++20時期了

Core knowledge

在這之前要先介紹一個核心知識,沒有這個都辦不到

1
2
3
4
5
6
7
8
9
10
11
12
// C++17
template<typename T>
void test() {
std::cout << __PRETTY_FUNCTION__ << '\n';
}

// C++20
#include <source_location>
template <typename T>
void test() {
std::cout << std::source_location::current().function_name() << "\n";
}

__PRETTY_FUNCTION__是gcc/clang特有的,Visual C++有等價的__FUNCSIG__,不過在C++20之後,用std::source_location::current().function_name()就好,接下來的問題就變成了,如何將struct的information成為funcion name的一部分`

Non-type Template Parameters in C++20

在C++20當中,NTTP能居受的種類更多了,由於這樣,很難推斷出類型,所以template <auto> 發揮出功用了

1
2
3
4
5
6
7
8
9
10
11
12
13
template <auto V>
void test() {
std::cout << std::source_location::current().function_name() << "\n";
}

struct obj {
int field;
};

int main() {
test<&obj::field>();
}
`

輸出結果

1
void test() [with auto V = &obj::field]

到目前為止,這段程式碼還不是太有用,因為印出了太多不需要的東西,所以要對輸出Function Name做處理

Process Function Name

用constexpr std::string_view做處理

1
2
3
4
5
6
7
8
9
10
11
template <auto V>
constexpr std::string_view get_name()
{
std::string_view funcname = std::source_location::current().function_name();
auto pos = funcname.find("=");
funcname = funcname.substr(pos);
pos = funcname.find("::");
funcname = funcname.substr(pos + 2);
pos = funcname.find(";");
return funcname.substr(0, pos);
}

MSVC和gcc/clang的處理方式不同,要個別處理

Reference