软件目录结构正式


C语言中如何运用宏C(和C++)中之宏(Macro)属于编译器预处理的范畴,属于编译期概念(而无运行期概念)。下边对常境遇的宏的使用问题做了概括总括。

title: 软件目录结构正式
tags: python
author: Chinge Yang

关于#和#\

当C语言的宏中,#的效用是用这前边的宏参数举行字符串化操作(Stringfication),简单说固然是以对它们所引用的宏变量 通过轮换后以其左右各国增长一个双引号。比如下边代码中之宏:

#define WARN_IF(EXP)    do{ if (EXP)    fprintf(stderr, "Warning: " #EXP "/n"); }   while(0)

这实际上运用被会冒出下边所著之交替过程:

WARN_IF (divider == 0);

被替换为


do {

if (divider == 0)

fprintf(stderr, "Warning" "divider == 0" "/n");

} while(0);

这样每回divider(除数)为0的时节就是会于正式错误流及输出一个指示音信。

而##深受称连接符(concatenator),用来用片只Token连接为一个Token。注意这里连的靶子是Token就行,而不一定 是宏的变量。比如您只要做一个菜系项命令名和函数指针组成的结构体的累累组,并且愿意于函数号称与菜单项命令名中暴发直观的、名字上之关联。那么下面的代码就十分实用:

struct command

{

char * name;

void (*function) (void);

};

#define COMMAND(NAME) { NAME, NAME ## _command }

// 然后你就用一些预先定义好的命令来方便的初始化一个command结构的数组了:

struct command commands[] = {

COMMAND(quit),

COMMAND(help),

...

}

COMMAND宏以此担任一个代码生成器的效用,这样好于早晚水准达压缩代码密度,直接地为足以减去非在意所导致的失实。大家尚好n个##标记连接
n+1只Token,这么些特点也是#符所不备的。比如:

#define LINK_MULTIPLE(a,b,c,d) a##_##b##_##c##_##d

typedef struct _record_type LINK_MULTIPLE(name,company,position,salary);

// 这里这个语句将展开为:

//  typedef struct _record_type name_company_position_salary;

date: 2016-12-21

关于…的使用

…在C宏中称为Variadic Macro,也就算是更换参宏。比如:

#define myprintf(templt,...) fprintf(stderr,templt,__VA_ARGS__)

// 或者

#define myprintf(templt,args...) fprintf(stderr,templt,args)

首先单宏中由于无针对性易参起名,我们为此默认的巨大__VA_ARGS__来替她。第二只宏
中,大家显式地命名变参为args,那么大家以宏定义中不怕可就此args来代指变参了。同C语言的stdcall一样,变参必须作为参数表的太有相同宗有
现。当下边的宏中我们只可以提供第一独参数templt时,C标准要求我们须写成:

myprintf(templt,);

的格局。这时的交替过程也:

myprintf("Error!/n",);

替换为:


fprintf(stderr,"Error!/n",);

立是一个语法错误,不可知正常编译。这多少个题材一般发生半点个缓解办法。首先,GNU
CPP提供的缓解智允许地方的宏调用写成:

myprintf(templt);

倘诺其用会晤为通过轮换变成:

fprintf(stderr,"Error!/n",);

相当显著,这里还会发出编译错误(非本例的某些情况下不谋面生编译错误)。除了这种方法外,c99和GNU
CPP都帮忙下的宏定义情势:

#define myprintf(templt, ...) fprintf(stderr,templt, ##__VAR_ARGS__)

这时,##是连续符号充当的用意就是是当__VAR_ARGS__为空的时,消除后面的深逗号。那么此时底翻过程如下:

myprintf(templt);

被转化为:


fprintf(stderr,templt);

这般如果templt合法,将无相会生出编译错误。
这里列有了有些宏使用中易出错的地点,以及当的接纳模式。

软件目录结构正式

@(学习)[python]

声称:此文是依照Alex教学中整而来。

[TOC]

左的嵌套-Misnesting

宏的定义不自然要起完全的、配对的括号,不过以避免失误并且加强可读性,最好制止这样以。

1.为啥而设计好目录结构?

由操作符优先级引起的问题-Operator Precedence Problem

由于宏只是概括的替换,宏的参数假如是复合结构,那么通过轮换下可能出于各个参数之间的操作符优先级高于单个参数内部各级有内互相效能的操作符优先级,假使我们毫不括号爱护各样宏参数,可能会晤有预想不到的意况。比如:

#define ceil_div(x, y) (x + y - 1) / y

那么

a = ceil_div( b & c, sizeof(int) );

用于转化为:

a = ( b & c  + sizeof(int) - 1) / sizeof(int);

// 由于+/-的优先级高于&的优先级,那么上面式子等同于:

a = ( b & (c + sizeof(int) - 1)) / sizeof(int);

当下明明不是调用者的初衷。为了避免这种情景暴发,应当多写四只括号:

#define ceil_div(x, y) (((x) + (y) - 1) / (y))

1.企划类目录结构

“项目目录结构”是属于”可读性和可维护性”的规模,大家设计一个层次显著的目结构,就是为达成以下简单碰:

  • 可读性强:
    不熟稔这项目标代码的人口,一眼便可知看掌握目录结构,知道程序启动脚论是何人,测试目录在何处,配置文件于何处之类。从而充足便捷的领会这些路。就比如咱所使用的Linux系统一样,固定约定熟成的目录代表不同之功能等。
  • 可维护性高:
    定义好协会规则后,维护者就可知好引人注目地精通,新增的何人文件与代码应该置身什么目录之下。这一个利益是,随着岁月之缓,代码/配置的面追加,项目布局不会晤混杂,仍能协会优秀。

故此,保持一个层次显明的目录结构是起必不可少之。更何况协会一个优质的工目录,其实是同宗好简短的政。

消除多余的支行-Semicolon Swallowing

一般说来情况下,为了要函数模样的巨大在表面上看起如一个平日的C语言调用一样,平时状态下我们于宏的后边长一个分号,比如下边的带参宏:

MY_MACRO(x);

可是假如是下的气象:

#define MY_MACRO(x) {    /* line 1 */    /* line 2 */    /* line 3 */ }

//...

if (condition())

MY_MACRO(a);

else

{...}

如此这般相会出于多有的特别分号发生编译错误。为了避免这种情状出现又保障MY_MACRO(x);的这种写法,我们用拿宏定义为这种样式:

#define MY_MACRO(x) do {

/* line 1 */    /* line 2 */    /* line 3 */ } while(0)

诸如此类要保证总是用分号,就非会合起其他问题。

2.索引社团情势

至于怎样协会一个比好的Python工程目录结构,已经发有获取了共识的目录结构。在Stackoverflow的斯题材及,能来看我们对Python目录结构的座谈。
这边面说的都生好了。
一经你的品类名为也foo, 相比较提议之尽方便连忙目录结构这样即使充足了:
Foo/
|– bin/
| |– foo
|
|– foo/
| |– tests/
| | |– init.py
| | |– test_main.py
| |
| |– init.py
| |– main.py
|
|– conf/
| | –foo.conf
|– docs/
| |– abc.rst
|
|– setup.py
|– requirements.txt
|– README

粗略解释一下:

bin/: 存放项目的片可执行文件,当然你可于名script/之类的也行。
foo/: 存放项目标有源代码。(1)
源代码中之拥有模块、包都应该在这目录。不要放顶层目录。(2)
其子目录tests/存放单元测试代码; (3) 程序的入口最好命名吧main.py。
conf/: 存放配置文件。
docs/: 存放一些文档。
setup.py: 安装、部署、打包之台本。
requirements.txt: 存放软件依赖之标Python包列表。
README: 项目表明文件。
除了,有一对方案被出了更为多之情。比如LICENSE.txt,ChangeLog.txt文件等。

关于README的内容
斯理应是每个项目还当有的一个文本,目标是力所能及大概描述该类型的信息,让读者很快精晓此类型。
它们用征以下几单事项:

软件定位,软件的基本功效。
运作代码的情势: 安装环境、启动命令等。
粗略的以表达。
代码目录结构表明,更详细点可以阐明软件的基本原理。
普遍问题求证。

在软件开发初期,由于开发过程遭到上述内容恐怕不明朗要爆发变化,并无是一定固然当平开便将兼具信息还补全。然而在项目终止之时节,是索要写作这样的一个文档的。

可以参考Redis源码中Readme的写法,这多少个中简洁而清晰的描述了Redis功效及源码结构。

关于requirements.txt和setup.py
setup.py

一般来说,用setup.py来管理代码的打包、安装、部署问题。业界标准的写法是用Python流行的打包工具setuptools来管理这些事情。这种方式普遍应用于开源项目中。不过这里的核心思想不是用标准化的工具来解决这些问题,而是说,一个项目一定要有一个安装部署工具,能快速便捷的在一台新机器上将环境装好、代码部署好和将程序运行起来。

setup.py能够将那些工作自动化起来,提升效用、缩短失误的几率。”复杂的物自动化,能自动化的东西自然要自动化。”是一个坏好的习惯。
setuptools的文档相比较大,刚沾的话语,可能无太好找到切入点。学习技能之法就是看旁人是怎用的,可以参照一下Python的一个Web框架,flask是咋样勾勒的:
setup.py
当,简单点好写单装脚本(deploy.sh)替代setup.py也未尝不可。

requirements.txt

这个文件存在的目的是:
方便开发者维护软件的包依赖。将开发过程中新增的包添加进这个列表中,避免在setup.py安装依赖时漏掉软件包。
方便读者明确项目使用了哪些Python包。
这个文件的格式是每一行包含一个包依赖的说明,通常是flask>=0.10这种格式,要求是这个格式能被pip识别,这样就可以简单的通过 pip install -r requirements.txt来把所有Python包依赖都装好了。

至于配置文件的采纳方法

注意,在上面的目录结构中,没有将conf.py放在源码目录下,而是放在conf/目录下。
很多项目对配置文件的使用做法是:
配置文件写在一个或多个python文件中,比如此处的conf.py。
项目中哪个模块用到这个配置文件就直接通过import conf这种形式来在代码中使用配置。
这种做法我不太赞同:
这让单元测试变得困难(因为模块内部依赖了外部配置)
另一方面配置文件作为用户控制程序的接口,应当可以由用户自由指定该文件的路径。
程序组件可复用性太差,因为这种贯穿所有模块的代码硬编码方式,使得大部分模块都依赖conf.py这个文件。
所以,更好的方式是,
模块的配置都是可以灵活配置的,不受外部配置文件的影响。
程序的配置也是可以灵活控制的。
能够佐证这个思想的是,用过nginx和mysql的同学都知道,nginx、mysql这些程序都可以自由的指定用户配置。
所以,不应当在代码中直接import conf来使用配置文件。上面目录结构中的conf.py,是给出的一个配置样例,不是在写死在程序中直接引用的配置文件。可以通过给main.py启动参数指定配置路径的方式来让程序读取配置内容。当然,这里的conf.py你可以换个类似的名字,比如settings.py。或者你也可以使用其他格式的内容来编写配置文件,比如settings.yaml之类的。

Duplication of Side Effects

这里的Side
Effect是依宏在展开的上对这参数可能展开反复伊娃(Eva)luation(也即是取值),但是一旦是宏参数是一个函数,那么就是生或受调用多次所以达到不雷同的结果,甚至会生更严重的左。比如:

#define min(X,Y) ((X) > (Y) ? (Y) : (X))

//...

c = min(a,foo(b));

此刻foo()函数就吃调用了点儿差。为了缓解者地下的问题,大家应该这样形容min(X,Y)这些宏:

#define min(X,Y) ({  typeof (X) x_ = (X);    typeof (Y) y_ = (Y);    (x_ < y_) ? x_ : y_; })

({…})的图是以内的几乎漫漫告句子被最后一漫漫的价值重回,它吗同意以里面宣称变量(因为她经过大括哀号做了一个有的Scope)。

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图