2013-05-09 73 views
1

摘要:我想看看我是否可以重构具有规律,以使其更易于更新和维护一些C++代码。重构规则的C++代码模式

详细

我有一个创建线程本地计数器来跟踪统计的程序执行过程中的一些代码。目前,当统计信息添加到源代码中时,需要更新5件事情:计数器线程本地声明,计数器总计声明,重置线程计数器的函数,将线程计数器添加到总数的函数,和打印功能。

的代码是类似以下内容:

// Adding a statistic named 'counter' 

// Declaration of counter 
__thread int counter = 0; 
int total_counter = 0; 

// In reset function 
counter = 0; 

// In add function 
total_counter += counter; 

// In print function 
printf("counter value is: %d\n", total_counter); 

我可以看到一个宏可以在柜台做类似的声明创建:

#define STAT(name) __thread int name; \ 
        int total_##name; 

但我没有想到如何将其扩展到更新addreset功能。理想情况下,我想输入诸如STAT(counter)之类的东西,并具有用于管理统计信息的所有声明和功能。

编辑

我已经在代码更新的统计数据宏。这样的东西,如STAT_INC(counter)会增加本地计数器值。然后当线程完成执行时,线程本地值将被添加到整体总数中。所以每个统计的名字都很重要,这就是为什么一个数组不适合我的原因。因为真正的计数器名称是cache_hit,比counter[2]更有意义,我不想失去为创建的统计信息指定任意名称的能力。只是为了简化我在声明统计信息时必须编写的代码量。

+2

使用数组(或向量,管他呢)。 – 2013-05-09 18:55:26

回答

1

这使得或多或少你在你的问题封装在一个模板类描述了:

enum StatNames { 
    STAT_rx_bytes, 
    STAT_tx_bytes, 
    //..., 
}; 

template <StatNames SN> 
class Stat { 
    static const char *name_; 
    static __thread int x_; 
    static int total_; 

public: 
    Stat(const char *name) { name_ = name; } 
    static void reset() { x_ = 0; } 
    static void add() { total_ += x_; } 
    static void print() { 
     std::cout << name_ << " value is: " << total_ << "\n"; 
    } 
    static int & x() { return x_; } 
    static int total() { return total_; } 
}; 

template <StatNames SN> const char * Stat<SN>::name_; 
template <StatNames SN> __thread int Stat<SN>::x_; 
template <StatNames SN> int Stat<SN>::total_; 

#define STAT(name) Stat<STAT_##name> name(#name) 

然后,您可以编写代码如下所示:

STAT(rx_bytes); 

void * test (void *) 
{ 
    rx_bytes.x() += 4; 
    rx_bytes.add(); 
    std::cout << pthread_self() << ": " << rx_bytes.x() << "\n"; 
    return 0; 
} 

int main() 
{ 
    pthread_t t[2]; 
    pthread_create(&t[0], 0, test, 0); 
    pthread_create(&t[1], 0, test, 0); 
    pthread_join(t[0], 0); 
    pthread_join(t[1], 0); 
    rx_bytes.print(); 
} 
1

(没人接7分钟后...我得到的礼物!)

所以基本上你不想五个独立的命名变量。您可以使用数组或向量:

int counters[5]; 

那么它很容易更新某个计数器:与所有其他变量

class Counter { 
    int counters[5]; 
    void update_nth(int n) 
    { 
     counters[n]++; 
    } 
}; 

同样。

+0

我不认为这正是我正在寻找的。在我的问题中,我可能没有解释得很好。我希望能够用'foo'或'bar'等任意名称来声明计数器。然后我有一些函数在某些时候已经被调用来合计线程本地'foo'值。我可能不明白你的答案,但我不认为它允许我用任意名称定义新变量(这对于让代码自我记录很重要)。 – 2013-05-09 19:31:55

+0

@Gabriel如果你真的想重构一些代码,那么你就不能有名字**和**自动/统一更新(除非你把所有的东西都封装在一个'switch-case'中,但这是一个彻底的失败) – 2013-05-09 19:33:26

+0

感谢您的评论和答复。即使我不能比我现在做得更好,知道我不会错过简单的东西是有用的。 – 2013-05-09 19:42:06

1

跟进H2CO3的回答,有一个常见的成语,看起来像这样:

enum CounterEnums { 
    MyFirstCounter, 
    MySecondCounter, 
    // ... add new counter names here ... 
    NumCounters 
}; 
class Counter { 
    int counters[NumCounters]; 
public: 
    void update(int n) { counters[n]++; } 
}; 

现在,你可以轻松地添加另一个柜台,只是把它NumCounters之前。现在,你可以宣布你的情况下,是这样的:

Counter totals; // global 
boost::thread_specific_pointer<Counter> counters; // per-thread 

,并使用

counters->update(MyFirstCounter); 

(你还需要一些方法来更新totals和零您的每个线程的柜台,但我将......留给读者作为练习)。