最初的大多数代码都很冗长。我决定使用宏来简洁明了。如果使用像这样的宏是不好的做法,我在徘徊。我个人认为使用宏看起来更干净,但是如果宏隐藏了名称和函数,有些人可能会对我如何实现我的方法感到困惑。
原始代码
...
namespace ConwaysGameOfLife
{
class Grid
{
private:
//Members
...
using set_of_ints = std::unordered_set<int>;
using set_of_sizes = std::unordered_set<std::size_t>;
//Members to be used in Macros
set_of_sizes cells_at_t_minus_one;
set_of_sizes cells_at_t;
...
private:
//Functions
...
//Function showing Lengthy Conditionals
void rule(const std::size_t& cell_position)
{
std::size_t live_neighbors{0};
for(const auto& neighbor_offset : neighbor_offsets)
/*! Total Neighbors !*/
{
//Lengthy Conditional
if(cells_at_t_minus_one.find(cell_position + neighbor_offset) != cells_at_t.end())
{
live_neighbors++;
}
}
//Lengthy Conditional
if(cells_at_t_minus_one.find(cell_position) != cells_at_t.end() and live_neighbors < 2)
/*! Underpopulation !*/
{
cells_at_t.erase(cell_position);
}
//Lengthy Conditional
else if(cells_at_t_minus_one.find(cell_position) != cells_at_t.end() and (live_neighbors == 2 or live_neighbors == 3))
/*! Aging of a Cell !*/
{
cells_at_t.insert(cell_position);
}
//Lengthy Conditional
else if(cells_at_t_minus_one.find(cell_position) == cells_at_t.end() and live_neighbors == 3)
/*! Birth of a Cell !*/
{
cells_at_t.insert(cell_position);
}
//Lengthy Conditional
else if(cells_at_t_minus_one.find(cell_position) != cells_at_t.end() and live_neighbors > 3)
/*! Overpopulation !*/
{
cells_at_t.erase(cell_position);
}
}
public:
...
};
}
...
使用宏编码
...
#define neighbor cells_at_t_minus_one.find(cell_position + neighbor_offset)
#define cell cells_at_t_minus_one.find(cell_position)
#define dead cells_at_t.end()
#define is_live != dead
#define is_dead == dead
#define result second
namespace ConwaysGameOfLife
{
class Grid
{
private:
//Members
...
using set_of_ints = std::unordered_set<int>;
using set_of_sizes = std::unordered_set<std::size_t>;
//Members used in Macros
set_of_sizes cells_at_t_minus_one;
set_of_sizes cells_at_t;
...
private:
//Functions
...
void rule(const std::size_t& cell_position)
{
std::size_t live_neighbors{0};
for(const auto& neighbor_offset : neighbor_offsets)
/*! Total Neighbors !*/
{
//Macros used
if(neighbor is_live)
{
live_neighbors++;
}
}
//Macros used
if(cell is_live and live_neighbors < 2)
/*! Underpopulation !*/
{
cells_at_t.erase(cell_position);
}
//Macros used
else if(cell is_live and (live_neighbors == 2 or live_neighbors == 3))
/*! Aging of a Cell !*/
{
cells_at_t.insert(cell_position);
}
//Macros used
else if(cell is_dead and live_neighbors == 3)
/*! Birth of a Cell !*/
{
cells_at_t.insert(cell_position);
}
//Macros used
else if(cell is_live and live_neighbors > 3)
/*! Overpopulation !*/
{
cells_at_t.erase(cell_position);
}
}
public:
...
};
}
#undef neighbor
#undef cell
#undef dead
#undef is_live
#undef is_dead
#undef result
...
答案 0 :(得分:2)
我决定使用宏来简短明了。
翻译:我认为c ++不适合我,所以我发明了一种新的特定于域的语言,它有自己的语法。它看起来非常好,但由于它在预处理器中完全翻译,因此您只能表达我明确预期的概念。
如果使用像这样的宏是不好的做法,我就会徘徊。
如果通过不良做法,你的意思是,随着你的程序的增长和改变它将变得不可维护,那么是的,这是不好的做法。
如果不好的做法意味着你的同事会因为创建不可维护的代码而对你感到不满,那么这是不好的做法。
总结......
不仅是糟糕的做法,也是最糟糕的做法。
答案 1 :(得分:1)
此:
if (neighbor is_live) { ... }
绝对不可能理解。它看起来像是形式错误的代码,任何阅读它的人都会首先认为那里存在错误。忘记你的同行,这是你几个月后会回来的那种代码,并且在很长一段时间内都不会理解。
它还会产生很多问题,例如neighbor
,dead
,cell
,result
和is_live
等合理的名称可用作此特定程序的标识符。因此,如果您碰巧将它们用作标识符,那么您最终会遇到相当难以理解的错误。
考虑替代方案:
if (is_live(cell_position + neighbor_offset)) { ... }
我们只有一个函数is_live
才能做正确的事情:
bool is_live(size_t idx) { return !cells_at_t_minus_one.count(idx); }
这远非优越,因为:
neighbor_offset
更改为offset
,则该名称更改仅对neighbor_offset
的范围产生影响(在您的代码中甚至似乎没有使用) 。我不必更改宏的定义!请注意,您的实时检查是将一个容器(cells_at_t_minus_one
)中的迭代器与另一个容器(cells_at_t
)进行比较。
答案 2 :(得分:0)
关于宏的经验法则是:
如果可以,请避免使用它们。请改用内联函数或模板,因为宏只是文本的替代品,可能会导致重大问题。
如果必须使用它们,请将它们清楚地命名。尝试使用不太可能出现在其他任何地方的名称,因为宏只是文本的替代品。
2号是为了避免以下问题:
#define equals ==
int main()
{
bool equals = 2 equals 3;
if( equals )
printf( "2==3 is true?" );
}
最终为:
int main()
{
bool == = 2 == 3;
if( == )
printf( "2==3 is true?" );
}
处理宏时。