首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >字符串-在编译时进行分析实习

字符串-在编译时进行分析实习
EN

Stack Overflow用户
提问于 2018-05-11 09:08:20
回答 2查看 485关注 0票数 1

上下文

我正在开发一个检测分析器,它使您能够通过字符串来命名不同的度量。例如:

代码语言:javascript
运行
复制
MEASURE_SCOPE(text_rendering_code);
...
MEASURE_SCOPE(password_hashing);
...
MEASURE_START(system_call);
...
MEASURE_STOP(system_call);

其中宏的定义如下:

代码语言:javascript
运行
复制
#define MEASURE_START(name) save_start_event(get_timestamp(), #name);
#define MEASURE_STOP(name) save_stop_event(get_timestamp(), #name);
#define MEASURE_SCOPE(name) Profiling_Class object##name (#name);

class Profiling_Class{
    string name;
    Profiling_Class(string name){
        this->name = name; 
        save_start_event(get_timestamp(), name);
    }
    ~Profiling_Class(){save_end_event(get_timestamp(), this->name);}
}

save_start_eventsave_end_event只需将时间戳连同名称一起放入某个全局缓冲区,供以后使用(导出度量等)。

问题是:保存度量的名称以及度量本身是非常低效的。对MEASURE_STARTMEASURE_STOP也需要做大量的工作,因为检查它们的名称是否相同需要一个字符串比较。一个更好的解决方案是对字符串进行实习,即在某个存放所有字符串的数组中:

代码语言:javascript
运行
复制
std::vector<string> = {"text_rendering_code", "password_hashing", "system_call"};

并将测量宏中的字符串替换为数组中字符串的索引:

代码语言:javascript
运行
复制
MEASURE_SCOPE(0);
...
MEASURE_SCOPE(1);
...
MEASURE_START(2);
...
MEASURE_STOP(2);

这种方法需要更少的存储空间,并检查名称是否匹配成为一个简单的整数比较。另一方面,这是非常不友好的用户,因为他必须事先知道的索引,他想给他的测量。

问题

有没有一种方法可以保持MEASURE_SCOPE(text_rendering_code)的良好使用,并自动用更高效的MEASURE_SCOPE(0)来代替它呢?这将需要在编译时构建名称数组,有效地执行字符串。这个是可能的吗?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2018-05-11 09:19:45

相同的文字字符串不能保证是相同的,但是您可以从它构建类型,该类型可以比较相同的字符串(而不是比较字符串),类似于:

代码语言:javascript
运行
复制
// Sequence of char
template <char...Cs> struct char_sequence
{
    template <char C> using push_back = char_sequence<Cs..., C>;
};

// Remove all chars from char_sequence from '\0'
template <typename, char...> struct strip_sequence;

template <char...Cs>
struct strip_sequence<char_sequence<>, Cs...>
{
    using type = char_sequence<Cs...>;
};

template <char...Cs, char...Cs2>
struct strip_sequence<char_sequence<'\0', Cs...>, Cs2...>
{
    using type = char_sequence<Cs2...>;
};

template <char...Cs, char C, char...Cs2>
struct strip_sequence<char_sequence<C, Cs...>, Cs2...>
{
    using type = typename strip_sequence<char_sequence<Cs...>, Cs2..., C>::type;
};

// struct to create a aligned char array
template <typename chars> struct static_string;

template <char...Cs>
struct static_string<char_sequence<Cs...>>
{
    static constexpr char str[sizeof...(Cs)] = {Cs...};
};

template <char...Cs>
constexpr 
char static_string<char_sequence<Cs...>>::str[sizeof...(Cs)];

// helper to get the i_th character (`\0` for out of bound)
template <std::size_t I, std::size_t N>
constexpr char at(const char (&a)[N]) { return I < N ? a[I] : '\0'; }

// helper to check if the c-string will not be truncated
template <std::size_t max_size, std::size_t N>
constexpr bool check_size(const char (&)[N])
{
    static_assert(N <= max_size, "string too long");
    return N <= max_size;
}

// Helper macros to build char_sequence from c-string
#define PUSH_BACK_8(S, I) \
    ::push_back<at<(I) + 0>(S)>::push_back<at<(I) + 1>(S)> \
    ::push_back<at<(I) + 2>(S)>::push_back<at<(I) + 3>(S)> \
    ::push_back<at<(I) + 4>(S)>::push_back<at<(I) + 5>(S)> \
    ::push_back<at<(I) + 6>(S)>::push_back<at<(I) + 7>(S)>

#define PUSH_BACK_32(S, I) \
        PUSH_BACK_8(S, (I) + 0) PUSH_BACK_8(S, (I) + 8) \
        PUSH_BACK_8(S, (I) + 16) PUSH_BACK_8(S, (I) + 24)

#define PUSH_BACK_128(S, I) \
    PUSH_BACK_32(S, (I) + 0) PUSH_BACK_32(S, (I) + 32) \
    PUSH_BACK_32(S, (I) + 64) PUSH_BACK_32(S, (I) + 96)

// Macro to create char_sequence from c-string (limited to 128 chars)
#define MAKE_CHAR_SEQUENCE(S) \
    strip_sequence<char_sequence<> \
    PUSH_BACK_128(S, 0) \
    >::type::template push_back<check_size<128>(S) ? '\0' : '\0'>

// Macro to return an static c-string
#define MAKE_STRING(S) \
    aligned_string<MAKE_CHAR_SEQUENCE(S)>::str

所以

代码语言:javascript
运行
复制
MEASURE_SCOPE(MAKE_STRING("text_rendering_code"));

仍将返回与直接比较相同的指针。

您可以修改宏MEASURE_SCOPE以直接包含MAKE_STRING

gcc有一个扩展来简化MAKE_STRING

代码语言:javascript
运行
复制
template <typename CHAR, CHAR... cs>
const char* operator ""_c() { return static_string<cs...>{}::str; }

然后

代码语言:javascript
运行
复制
MEASURE_SCOPE("text_rendering_code"_c);
票数 1
EN

Stack Overflow用户

发布于 2018-05-11 09:18:19

我只能猜出你的意思,因为你没有给出足够的细节,它们很重要。

一种可能的方法是使用您自己的生成器生成一些特定的C或代码。请记住,可以生成项目的一些C或C++代码(这是一个粗糙的表单或元编程;Qt mocRPCGEN野牛大口是C++或C生成器的典型示例,但您可以轻松地创建自己的生成器,请参阅这里;可能需要使用一些脚本语言,比如Python诡计AWK、.,甚至C++),而< code >C11可以处理这个问题(例如,Makefile中的一些特殊规则或菜谱)。

然后,您可以编写一个非常简单的生成程序,收集代码中所有出现的MEASURE_SCOPEMEASURE_STARTMEASURE_STOP宏调用(项目的*.cpp文件)。这对代码来说非常简单:您可以逐行读取所有.cpp文件并查找MEASURE_SCOPE (等等)。后面是空格,然后是(

使用您的实习生字符串生成程序-dealing -可能会发出一个大的头文件measure-generated.h,例如

代码语言:javascript
运行
复制
// in generated header
#define MEASURE_POINT_system_call 1
#define MEASURE_POINT_password_hashing 2

(也许您想要生成一些大型的enum )

它还会发出一个measure-generated-array.cpp文件,如

代码语言:javascript
运行
复制
// generated code
const char* measure_array[] = {
  NULL,
  "system_call",
  "password_hashing",
  /// etc....
  NULL,
};

然后你可以在你的一些标题中

代码语言:javascript
运行
复制
#define MEASURE_SCOPE(X) measure_array[MEASURE_POINT_##X]

等,使用预处理技巧,如绞线和/或串连

另见

这将需要在编译时构建名称数组,有效地执行字符串。这个是可能的吗?

是的,当然。在您自己的C++生成器中这样做,它知道我建议的所有项目*.cpp文件。您可以在C++时生成构建文件。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/50288847

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档