【永利官网ylg客户端】宏定义中的##操作符和… and _ _VA_ALANDGS_ _

match ordering {
    // This is an error, since if a new variant is added,
    // this would suddenly break on an upgrade of the compiler.
    Relaxed | Release | Acquire | AcqRel | SeqCst => {
        /* logic */
    }
}

属性是跟与之关联的对象的类型相关。例如,一个Circle(圆)对象有一个Diameter(直径)属性,但Line
(线)对象就没有。根据不同的对象类型,属性也各不一样,下面的代码在拾取CIRCLE(圆)图元时会出错:

常见用法

XNAME(4)

#[non_exhaustive] 结构,枚举和变体

(vlax-safearray-fill safearray
‘element-values) 
指定值给安全数组内的多个元素。如果提供的参数不是一个数组,将返回ActiveX错误。你需要将对该函数的调用封装到错误捕获内以使这个错误能得到适当处理。

packed

让指定的结构结构体按照一字节对齐,测试:

//不加packed修饰
typedef struct {
    char    version;
    int16_t sid;
    int32_t len;
    int64_t time;
} Header;

//计算长度
NSLog(@"size is %zd",sizeof(Header));
输出结果为:
2016-07-22 11:53:47.728 Study[14378:5523450] size is 16

可以看出,默认系统是按照4字节对齐

//加packed修饰
typedef struct {
    char    version;
    int16_t sid;
    int32_t len;
    int64_t time;
}__attribute__ ((packed)) Header;

//计算长度
NSLog(@"size is %zd",sizeof(Header));
输出结果为:
2016-07-22 11:57:46.970 Study[14382:5524502] size is 15

用packed修饰后,变为1字节对齐,这个常用于与协议有关的网络传输中.

建议你看看C语言相关的预处理命令部分
对于这 #define 是宏定义命令,分为无参数宏定义 和 有参数宏定义,你这个属于有参数的宏定义;
对于有参数的宏定义,参数部分 应该为 要替换为的部分的变量;
你这里 #define dbg_msg(...)   参数部分 是 ... 没有这种用法
例如:
#include<stdio.h>
#define dbg_msg(__FUNCTION__,__LINE__,__VA_ARGS__)  {printf("### [%s:%d] ", __FUNCTION__, __LINE__); printf(__VA_ARGS__);}

void main()
{
    char a[] = "123";
    int b = 5;
    char c[]="456"; 
    dbg_msg(a,b,c);  //宏定义要替换的部分,在编译前预处理器就会将这个地方替换为 目标字符串,之后才进行编译
}

增加到标准库的函数

(vlax-create-object program-id)

attribute是GNU C特色之一,在iOS用的比较广泛.系统中有许多地方使用到.
attribute可以设置函数属性(Function Attribute )、变量属性(Variable
Attribute )和类型属性(Type Attribute)等.

    double y;

详情可查看更新说明:

(vlax-remove-cmd “example1”) 

(vlax-remove-cmd “example2”) 
nil

变量属性(Variable Attribute)

  • aligned
  • packed

Like the # operator, the ## operator can be used in the replacement
section of a function-like macro.Additionally, it can be used in the
replacement section of an object-like macro. The ## operator combines
two tokens into a single token. 

包括有bang!() macros, 例如:

尽管变体和安全数组数据类型这个话题在本书的前几节已经讲过,但在Visual
LISP的ActiveX领域中它们具有相当重要的位置,值得我们用一整章的篇幅来专门讲解。我们将首先简要了解他们是什么,然后开始探索如何通过Visual
LISP函数来使用它们。

objc_designated_initializer

用来修饰类的designated
initializer初始化方法,如果修饰的方法里没有调用super类的 designated
initializer,编译器会发出警告。可以简写成NS_DESIGNATED_INITIALIZER

这篇文章讲的很好,建议参考这个.
https://yq.aliyun.com/articles/5847

printf(“weight = %d, shipping = $%.2fn”, wt, sp);

match ordering {
    Relaxed | Release | Acquire | AcqRel | SeqCst => { /* ... */ }
    // OK; if more variants are added, nothing will break.
    _ => { /* logic */ }
}

示例:

Clang特有的

  • availability
  • overloadable

 

#[non_exhaustive]
pub enum Ordering { Relaxed, Release, Acquire, AcqRel, SeqCst }

示例:
(vl-file-size “c:\myfile1.txt”); 返回
125523 (大约 124 Kb) 

objc_requires_super

这个属性要求子类在重写父类的方法时,必须要重载父类方法,也就是调用super方法,否则警告.示例如下:

@interface ViewController : UIViewController

- (void)jump __attribute__((objc_requires_super));

@end

- (void)jump{
    NSLog(@"父类必须先执行");
}


@interface SGViewController : ViewController

@end

@implementation SGViewController
- (void)jump{
    NSLog(@"子类才能再执行");
}
@end

警告如下:

永利官网ylg客户端 1

Paste_Image.png

预处理连接符:##操作符

(文/开源中国)    

(vlax-invoke-method object method [arguments]…) 或 
(vla-method object arguments) 或 
(vlax-invoke object method [arguments] …) 

visibility

语法:

__attribute__((visibility("visibility_type")))

其中,visibility_type 是下列值之一:

  • default
    假定的符号可见性可通过其他选项进行更改。缺省可见性将覆盖此类更改。缺省可见性与外部链接对应。

  • hidden
    该符号不存放在动态符号表中,因此,其他可执行文件或共享库都无法直接引用它。使用函数指针可进行间接引用。

  • internal
    除非由 特定于处理器的应用二进制接口 (psABI)
    指定,否则,内部可见性意味着不允许从另一模块调用该函数。

  • protected
    该符号存放在动态符号表中,但定义模块内的引用将与局部符号绑定。也就是说,另一模块无法覆盖该符号。

  • 除指定 default
    可见性外,此属性都可与在这些情况下具有外部链接的声明结合使用。
    您可在 C 和 C++ 中使用此属性。在 C++
    中,还可将它应用于类型、成员函数和命名空间声明。

系统用法:

//  UIKIT_EXTERN     extern
 #ifdef __cplusplus
 #define UIKIT_EXTERN        extern "C" __attribute__((visibility ("default")))
 #else
 #define UIKIT_EXTERN            extern __attribute__((visibility ("default")))
 #endif

Then the four strings are concatenated, reducing the call to this:

macro_rules! make_item { ($name:ident) => { fn $name(); } }

extern {
    make_item!(alpha);
    make_item!(beta);
}

参数:
Symbol – 变体值
Type – 要转换到的数据类型数字或枚举

NSObject

@property (nonatomic,strong) __attribute__((NSObject)) CFDictionaryRef myDictionary;

CFDictionaryRef属于CoreFoundation框架的,也就是非OC对象,加上attribute((NSObject))后,myDictionary的内存管理会被当做OC对象来对待.

1.Preprocessor Glue: The ## Operator

// alpha/lib.rs:

#[non_exhaustive]
struct Foo {
    pub a: bool,
}

enum Bar {
    #[non_exhaustive]
    Variant { b: u8 }
}

fn make_foo() -> Foo { ... }
fn make_bar() -> Bar { ... }

// beta/lib.rs:

let x = Foo { a: true }; //~ ERROR
let Foo { a } = make_foo(); //~ ERROR

// `beta` will still compile when more fields are added.
let Foo { a, .. } = make_foo(); //~ OK


let x = Bar::Variant { b: 42 }; //~ ERROR
let Bar::Variant { b } = make_bar(); //~ ERROR
let Bar::Variant { b, .. } = make_bar(); //~ OK
                   // -- `beta` will still compile...

类继承
对象模型通常都是从根或源对象开始。在AutoCAD中,源对象是AutoCAD
Application
(AutoCAD应用程序)对象,也被称之为AcadApplication对象。它提供了基本的属性、方法、事件和来自所有其它对象和集合构成的集合。例如,AcadApplication对象有一集合为Documents(即Documents集合),在其中对应有一个或多个Document对象。每一Document对象有它自己的对象、集合、属性和方法以及其它东西。

constructor / destructor

意思是:
构造器和析构器;constructor修饰的函数会在main函数之前执行,destructor修饰的函数会在程序exit前调用.
示例如下:

int main(int argc, char * argv[]) {
    @autoreleasepool {
        NSLog(@"main");
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}

__attribute__((constructor))
void  before(){
    NSLog(@"before main");
}

__attribute__((destructor))
void  after(){
    NSLog(@"after main");
}

//在viewController中调用exit
- (void)viewDidLoad {
    [super viewDidLoad];

    exit(0);
}
输出如下:

2016-07-21 21:49:17.446 Study[14162:5415982] before main
2016-07-21 21:49:17.447 Study[14162:5415982] main
2016-07-21 21:49:17.534 Study[14162:5415982] after main

注意点:

  • 程序退出的时候才会调用after函数,经测试,手动退出程序会执行
  • 上面两个函数不管写在哪个类里,哪个文件中效果都一样
  • 如果存在多个修饰的函数,那么都会执行,顺序不定

实际上如果存在多个修饰过的函数,可以它们的调整优先级
代码如下:

int main(int argc, char * argv[]) {
    @autoreleasepool {
        NSLog(@"main");
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}

__attribute__((constructor(101)))
void  before1(){
    NSLog(@"before main - 1");
}
__attribute__((constructor(102)))
void  before2(){
    NSLog(@"before main - 2");
}

__attribute__((destructor(201)))
void  after1(){
    NSLog(@"after main - 1");
}
__attribute__((destructor(202)))
void  after2(){
    NSLog(@"after main - 2");
}

输出结果如下:
2016-07-21 21:59:35.622 Study[14171:5418393] before main - 1
2016-07-21 21:59:35.624 Study[14171:5418393] before main - 2
2016-07-21 21:59:35.624 Study[14171:5418393] main
2016-07-21 21:59:35.704 Study[14171:5418393] after main - 2
2016-07-21 21:59:35.704 Study[14171:5418393] after main - 1

注意点:

  • 括号内的值表示优先级,[0,100]这个返回时系统保留的,自己千万别调用.
  • 根据输出结果可以看出,main函数之前的,数值越小,越先调用;main函数之后的数值越大,越先调用.

当函数声明和函数实现分开写时,格式如下:

static void before() __attribute__((constructor));

static void before() {
    printf("beforen");
}

讨论:+load,constructor,main的执行顺序,代码如下:

+ (void)load{
    NSLog(@"load");
}
__attribute__((constructor))
void  before(){
    NSLog(@"before main");
}

输出结果如下:
2016-07-21 22:13:58.591 Study[14185:5421811] load
2016-07-21 22:13:58.592 Study[14185:5421811] before main
2016-07-21 22:13:58.592 Study[14185:5421811] main

可以看出执行顺序为:
load->constructor->main
为什么呢?
因为 dyld(动态链接器,程序的最初起点)在加载 image(可以理解成 Mach-O
文件)时会先通知 objc runtime 去加载其中所有的类,每加载一个类时,它的
+load 随之调用,全部加载完成后,dyld 才会调用这个 image 中所有的
constructor 方法,然后才调用main函数.

    return 0;

  • is_power_of_two用于无符号整数

集合和词典
集合是在相同父窗口中一组相似的对象。该容器有一个独特的名字,在大多数情况下,将提供自己的方法来访问所包含的对象。词典是一种特殊类型的集合,它允许你扩展你自己的词典。Visual
LISP并没有过多提供用于创建或处理集合的方法。它允许你遍历它、修改项目、添加或删除条目。词典允许你添加自己的词典并写入数据,你可遍历它、添加、修改和删除其条目,同样,你也可以添加
、修改和删除词典本身。

nonnull

编译器对函数参数进行NULL的检查,参数类型必须是指针类型(包括对象)
//使用

- (int)addNum1:(int *)num1 num2:(int *)num2  __attribute__((nonnull (1,2))){//1,2表示第一个和第二个参数不能为空
    return  *num1 + *num2;
}

- (NSString *)getHost:(NSURL *)url __attribute__((nonnull (1))){//第一个参数不能为空
    return url.host;
}

#define PR(X, …) printf(“Message” #X “: ” _ _VA_ARGS_ _)

Procedural macro attributes on items
in extern { ... } blocks 现在也被支持:

(vl-every 
  (function
    (lambda (filename) 
      (> (vl-file-size filename) 1024)
    )
  )
  (vl-directory-files nil nil 1) 

availability

官方例子:

- (CGSize)sizeWithFont:(UIFont *)font NS_DEPRECATED_IOS(2_0, 7_0, "Use -sizeWithAttributes:") __TVOS_PROHIBITED;

//来看一下 后边的宏
 #define NS_DEPRECATED_IOS(_iosIntro, _iosDep, ...) CF_DEPRECATED_IOS(_iosIntro, _iosDep, __VA_ARGS__)

define CF_DEPRECATED_IOS(_iosIntro, _iosDep, ...) __attribute__((availability(ios,introduced=_iosIntro,deprecated=_iosDep,message="" __VA_ARGS__)))

//宏展开以后如下
__attribute__((availability(ios,introduced=2_0,deprecated=7_0,message=""__VA_ARGS__)));
//ios即是iOS平台
//introduced 从哪个版本开始使用
//deprecated 从哪个版本开始弃用
//message    警告的消息

availability属性是一个以逗号为分隔的参数列表,以平台的名称开始,包含一些放在附加信息里的一些里程碑式的声明。

  • introduced:第一次出现的版本。

  • deprecated:声明要废弃的版本,意味着用户要迁移为其他API

  • obsoleted: 声明移除的版本,意味着完全移除,再也不能使用它

  • unavailable:在这些平台不可用

  • message:一些关于废弃和移除的额外信息,clang发出警告的时候会提供这些信息,对用户使用替代的API非常有用。

  • 这个属性支持的平台:ios,macosx。

简单例子:

//如果经常用,建议定义成类似系统的宏
- (void)oldMethod:(NSString *)string __attribute__((availability(ios,introduced=2_0,deprecated=7_0,message="用 -newMethod: 这个方法替代 "))){
    NSLog(@"我是旧方法,不要调我");
}

- (void)newMethod:(NSString *)string{
    NSLog(@"我是新方法");
}

效果:

永利官网ylg客户端 2

Paste_Image.png

//如果调用了,会有警告

永利官网ylg客户端 3

Paste_Image.png

For the second invocation, it expands to three arguments:

例如,用户可以编写以下类型:Foo = expand_to_type!(bar); 其中
expand_to_type 将是一个 procedural macro。

但是,如果在取对象的属性前先检验该属性是否有效时,代码的执行就能正常,如下面的例子所示:

cleanup

声明到一个变量上,当这个变量作用域结束时,调用指定的一个函数.如果不知道什么是作用域,请先学习一下.例子:

//这里传递的参数是变量的地址
void intCleanup(int *num){
    NSLog(@"cleanup------%d",*num);
}

- (void)test{
  int a __attribute__((cleanup(intCleanup))) = 10;
}

输出结果为:
2016-07-22 09:59:09.139 Study[14293:5495713] cleanup------10

注意点:

  • 指定的函数传递的参数是变量的地址
  • 作用域的结束包括:大括号结束、return、goto、break、exception等情况
  • 当作用域内有多个cleanup的变量时,遵守 先入后出 的栈式结构.

示例代码:

void intCleanup(int *num){
    NSLog(@"cleanup------%d",*num);
}

void stringCleanup(NSString **str){
    NSLog(@"cleanup------%@",*str);
}

void rectCleanup(CGRect *rect){
    CGRect temp = *rect;
    NSString *str = NSStringFromCGRect(temp);
    NSLog(@"cleanup------%@",str);
}


 int a __attribute__((cleanup(intCleanup))) = 10;
    {
        NSString *string __attribute__((cleanup(stringCleanup))) = @"string";
        CGRect rect __attribute__((cleanup(rectCleanup))) = {0,0,1,1};
    }


    输出结果为:
    2016-07-22 10:09:36.621 Study[14308:5498861] cleanup------{{0, 0}, {1, 1}}
2016-07-22 10:09:36.622 Study[14308:5498861] cleanup------string
2016-07-22 10:09:36.622 Study[14308:5498861] cleanup------10

讨论:如果修饰了某个对象,那么cleanup和dealloc,谁先执行?
测试代码如下:

void objectCleanup(NSObject **obj){
    NSLog(@"cleanup------%@",*obj);
}

- (void)viewDidLoad {
    [super viewDidLoad];
    ViewController *vc __attribute__((cleanup(objectCleanup))) = [[ViewController alloc] init];
}

- (void)dealloc{
    NSLog(@"dealloc");
}

输出结果如下:
2016-07-22 10:23:08.839 Study[14319:5502769] cleanup------<ViewController: 0x13fe881e0>
2016-07-22 10:23:08.840 Study[14319:5502769] dealloc

可以明显看出,cleanup先于对象的dealloc执行.

  • 在block中的用法:在block中使用,先看例子:

//指向block的指针,觉得不好理解可以用typeof
void blockCleanUp(void(^*block)()){
    (*block)();
}

 void (^block)(void) __attribute__((cleanup(blockCleanUp))) = ^{
        NSLog(@"finish block");
    };

这个好处就是,不用等到block最后才写某些代码,我们可以把它放在block的任意位置,防止忘记.

 

此版本中,以下函数成为const fn

常量和枚举
常量是特殊的数据类型。正如其名,它的值是不可改变的。有时被称为静态。通常来说,常量是由程序语言或由托管应用程序本身提供,作为一种方便的手段。例如,acByLayer常量在属性值中可以被256代替。名称值比整数值更好明白也更好记。例如,以下两个语句在功能上说是一致的。

noinline & always_inline

内联函数:内联函数从源代码层看,有函数的结构,而在编译后,却不具备函数的性质。内联函数不是在调用时发生控制转移,而是在编译时将函数体嵌入在每一个调用处。编译时,类似宏替换,使用函数体替换调用处的函数名。一般在代码中用inline修饰,但是能否形成内联函数,需要看编译器对该函数定义的具体处理

  • noinline 不内联
  • always_inline 总是内联
  • 这两个都是用在函数上

内联的本质是用代码块直接替换掉函数调用处,好处是:快代码的执行,减少系统开销.适用场景:

  • 这个函数更小
  • 这个函数不被经常调用

使用例子:

//函数声明
void test(int a) __attribute__((always_inline));

 

Macro and attribute 的改进

vl-every

书写格式

书写格式:attribute后面会紧跟一对原括弧,括弧里面是相应的attribute参数

__attribute__(xxx)

    PR(1, “x = %gn”, x);

  • 在 type
    contexts 中调用 procedural
    macros mac!() 。

Object – 任何Vla-对象

unavailable

告诉编译器该方法不可用,如果强行调用编译器会提示错误。比如某个类在构造的时候不想直接通过init来初始化,只能通过特定的初始化方法()比如单例,就可以将init方法标记为unavailable;

//系统的宏,可以直接拿来用
 #define UNAVAILABLE_ATTRIBUTE __attribute__((unavailable))

 #define NS_UNAVAILABLE UNAVAILABLE_ATTRIBUTE

@interface Person : NSObject

@property(nonatomic,copy) NSString *name;

@property(nonatomic,assign) NSUInteger age;

- (instancetype)init NS_UNAVAILABLE;

- (instancetype)initWithName:(NSString *)name age:(NSUInteger)age;

@end

永利官网ylg客户端 4

Paste_Image.png

//实际上unavailable后面可以跟参数,显示一些信息,如:

//系统的
 #define NS_AUTOMATED_REFCOUNT_UNAVAILABLE __attribute__((unavailable("not available in automatic reference counting mode")))

#define PR(…) printf(_ _VA_ARGS_ _)

  • The $m:meta 匹配器支持 arbitrary
    token-stream
    values.

(vl-list-exported-functions) 

函数属性(Function Attribute)

  • noreturn
  • noinline
  • always_inline
  • pure
  • const
  • nothrow
  • sentinel
  • format
  • format_arg
  • no_instrument_function
  • section
  • constructor
  • destructor
  • used
  • unused
  • deprecated
  • weak
  • malloc
  • alias
  • warn_unused_result
  • nonnull

#include <stdio.h>

也就是说,以下内容现在有效:

(setq sa (vlax-make-safearray vlax-vbString
‘(1 . 2) ‘(0 . 1) )) 
_$ (vlax-safearray-get-u-bound sa
1) 

第一维的上界的索引值为2。

enable_if

用来检查参数是否合法,只能用来修饰函数:

void printAge(int age)
__attribute__((enable_if(age > 0  && age < 120, "你丫太监?")))
{
    NSLog(@"%d",age);
}

表示只能输入的参数只能是 0 ~ 120左右,否则编译报错
报错如下:

永利官网ylg客户端 5

Paste_Image.png

Note how the PRINT_XN() macro uses the # operator to combine strings
and the ## operator to combine tokens into a new identifier.

macro_rules! accept_meta { ($m:meta) => {} }
accept_meta!( my::path );
accept_meta!( my::path = "lit" );
accept_meta!( my::path ( a b c ) );
accept_meta!( my::path [ a b c ] );
accept_meta!( my::path { a b c } );

提示! 当你选择对属性和方法用Get或Put中的任意一种形式时,你会发现用较长的形式(如vlax-put-property)比用较短的形式(如vla-put-color)更灵活。原因是通过从函数中分离出属性名称,你就可以定义函数和循环语句,它们可以接受一个属性列表及相关的数值,例如…

aligned

__attribute((aligned
(n))),让所作用的结构成员对齐在n字节自然边界上。如果结构中有成员的长度大于n,则按照最大成员的长度来对齐.例如:

不加修饰的情况

typedef struct
{
    char  member1;
    int   member2;
    short member3;
}Family;

//输出字节:
NSLog(@"Family size is %zd",sizeof(Family));
//输出结果为:
2016-07-25 10:28:45.380 Study[917:436064] Family size is 12

//修改字节对齐为1

typedef struct
{
    char  member1;
    int   member2;
    short member3;
}__attribute__ ((aligned (1))) Family;

//输出字节:
NSLog(@"Family size is %zd",sizeof(Family));
//输出结果为:
2016-07-25 10:28:05.315 Study[914:435764] Family size is 12

和上面的结果一致,因为 设定的字节对齐为1.而结构体中成员的最大字节数是int
4个字节,1 < 4,按照4字节对齐,和系统默认一致.

修改字节对齐为8

typedef struct
{
    char  member1;
    int   member2;
    short member3;
}__attribute__ ((aligned (8))) Family;

//输出字节:
NSLog(@"Family size is %zd",sizeof(Family));
//输出结果为:
2016-07-25 10:28:05.315 Study[914:435764] Family size is 16

这里 8 >
4,按照8字节对齐,结果为16,不知道字节对齐的可以看我的这篇文章http://www.jianshu.com/p/f69652c7df99

可是想了半天,也不知道这玩意有什么用,设定值小于系统默认的,和没设定一样,设定大了,又浪费空间,效率也没提高,感觉学习学习就好.

Message 2: x = 48.00, y = 6.9282

Rust
1.40.0 已经正式发布。该版本的亮点包括有 #[non_exhaustive] 和 macros!() and #[attribute]s 的改进。

提示!并不是所有的ActiveX枚举都在Visual
LISP中有提供。例如,标准的Decimal(十进制)或Short(短)数据类型并没有镜像为vlax-vbDecimal或vlax-vbShort。请参阅第六章学习更多关于数据类型的信息。

format

官方例子:NSLog

 #define NS_FORMAT_FUNCTION(F,A) __attribute__((format(__NSString__, F, A)))

format属性可以给被声明的函数加上类似printf或者scanf的特征,它可以使编译器检查函数声明和函数实际调用参数之间的格式化字符串是否匹配。该功能十分有用,尤其是处理一些很难发现的bug。对于format参数的使用如下
format (archetype, string-index, first-to-check)
第一参数需要传递“archetype”指定是哪种风格,这里是
NSString;“string-index”指定传入函数的第几个参数是格式化字符串;“first-to-check”指定第一个可变参数所在的索引.

The idea is that the final argument in an argument list for a macro
definition can be ellipses (that is, three periods)(省略号). If so,
the predefined macro _ _VA_ARGS_ _ can be used in the substitution
part to indicate what will be substituted for the ellipses. For example,
consider this definition:

以下函数和宏已经稳定:

调用关联于对象的方法并为方法提供任何所需的参数。如果成功,将返回结果。如果对象没有提供对应的方法,将会产生ActiveX错误。例如,在Text(文本)图元中调用“Offset”(偏移)方法,将会产生ActiveX错误。

objc_runtime_name

看到runtime是不是就感觉高大上,没错这个也跟运行时有关.作用是将将类或协议的名字在编译时指定成另一个.示例如下:

__attribute__((objc_runtime_name("NSObject")))
@interface SGObject :NSObject

@end

 //调用
 NSLog(@"%@",[SGObject class]);
 //输出
 2016-07-22 11:18:00.934 Study[14355:5516261] NSObject

可以用来做代码混淆.

更多请看官网:
https://gcc.gnu.org/onlinedocs/gcc-4.0.0/gcc/Function-Attributes.html

    return 0;

具体更新内容如下:

_$ (setq list1 (list 1 2 3 4)) 
(1 2 3 4) 
_$ (setq list2 nil) 
nil 
_$ (vl-every ‘= list2 list1) 

warn_unused_result

当函数或者方法的返回值很重要时,要求调用者必须检查或者使用返回值,否则编译器会发出警告提示

- (BOOL)availiable __attribute__((warn_unused_result))
{
   return 10;
}

警告如下:

永利官网ylg客户端 6

Paste_Image.png

PR(“weight = %d, shipping = $%.2fn”, wt, sp);

目前,函数式(mac!())和属性(#[mac])macros
都可以生成macro_rules!项目。

vl-every

noreturn

官方例子: abort() 和 exit()

该属性通知编译器函数从不返回值。当遇到类似函数还未运行到return语句就需要退出来的情况,该属性可以避免出现错误信息。

For the first invocation, _ _VA_ARGS_ _ expands to one argument:

#[non_exhaustive]在此上下文中的目的是确保可以随时间添加更多变体。这是通过防止其他包装箱从详尽模式实现match-ing上Ordering。也就是说,编译器将拒绝:

(vlax-make-safearray type dim1 [dim2]
…) 
创建一个安全数组,其数据类型为type,维度范围dim1,以此类推,可以指定额外的维度。不管什么原因,如果操作失败,语句将返回nil。 

objc_boxable

实现类似于NSNumber
的快速打包能力@(…),一般对于struct,union我们只能通过NSValue将其打包.
objc_boxable 可以帮助我们实现快速打包,示例如下:

//自定义结构体
typedef struct __attribute__((objc_boxable)){
    CGFloat x,y,width,height;
}SGRect;

 SGRect rect = {0,0,100,200};
 //这里直接打包成NSValue
 NSValue *value = @(rect);

 //这里我直接用系统的方法打印
 NSLog(@"%@",NSStringFromCGRect(value.CGRectValue));

 输出:
 2016-07-21 21:28:43.538 Study[14118:5408921] {{0, 0}, {100, 200}}

这样SGRect就具备快速打包功能了.

Here’s the output:

更重要的方面是,#[non_exhaustive]也可以附加到enum自身上。从标准库中获取的示例是Ordering

参数: 
Object – 一个 vla-对象 
Property – 与对象相关的有效属性。 

常见的系统用法

 

  • 在 procedural
    macros 中生成 macro_rules! items

参数:
Filename 字符串,指要删除的文件的名称。

objc_subclassing_restricted

因为某些原因,我们不希望这个类被继承,也就是 “最终”的类,用法如下:

__attribute__((objc_subclassing_restricted))
@interface ViewController : UIViewController


@end

如果继承了这个类,编译器会报错

永利官网ylg客户端 7

Paste_Image.png

int main(void)

  • todo!()

  • slice::repeat

  • mem::take

  • BTreeMap::get_key_value 和 HashMap::get_key_value

  • Option::as_deref, Option::as_deref_mut

  • Option::flatten

  • UdpSocket::peer_addr

  • {f32,f64}::to_be_bytes{f32,f64}::to_le_bytes{f32,f64}::to_ne_bytes{f32,f64}::from_be_bytes{f32,f64}::from_le_bytes,和{f32,f64}::from_ne_bytes

上例中定义的第二个函数提供了一个在Visual
LISP中对一个Excel工作表中的给定单元填色的方法。这可以通过使用Excel中已暴露接口的方法,这是通过首先加载Excel类型库实现的。这个以上显示的类型库项带有一个 msxl-前缀。

overloadable

用于c语言函数,可以定义若干个函数名相同,但参数不同的方法,调用时编译器会自动根据参数选择函数原型:

__attribute__((overloadable)) void print(NSString *string){
    NSLog(@"%@",string);
}

__attribute__((overloadable)) void print(int num){
    NSLog(@"%d",num);
}

//调用
print(10);
print(@"哈哈");

#include <math.h>

extern "C" {
    // Let's assume that this expands to `fn foo();`.
    #[my_identity_macro]
    fn foo();
}

(vlax-import-type-library 
  :tlb-filename name string 
  :methods-prefix    string 
  :properties-prefix string 
  :constants-prefix  string 

类型属性(Type Attributes)

  • aligned
  • packed
  • transparent_union,
  • unused,
  • deprecated
  • may_alias

#define XNAME(n) x ## n

  • Macros
    in extern { ... } blocks.

要使用类型库,就必须将其加载到内存,并确定接口指针已经定义。Visual
LISP提供了一毓函数用于加载和配置类型库接口。

objc_root_class

表示这个类是一个根类(基类),比如NSObject,NSProxy.

//摘自系统
//NSProxy
NS_ROOT_CLASS
@interface NSProxy <NSObject> {
    Class   isa;
}

//NSObject
__OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0)
OBJC_ROOT_CLASS
OBJC_EXPORT
@interface NSObject <NSObject> {
    Class isa  OBJC_ISA_AVAILABILITY;
}

would expand to the following:

可能使用命名空间最大的短处是会导致在操作系统或其宿主应用程序上的一些开销。为了管理好给定的命名空间,它必须有自己的内存地址范围和指针分配。这需要消耗额外的资源去跟踪和控制命名空间,这反过来又在必要时提供了直接的方法去访问它、卸载它或暂停它。

// glue.c — use the ## operator

当属性#[non_exhaustive]附加到struct或的变体时enum,它将防止定义它的板条箱外部的代码构造所述struct或变体。为了避免将来损坏,还防止其他包装箱在田地上进行彻底匹配。以下示例说明了beta取决于的错误alpha

(setq xlapp (vlax-get-or-create-object
“Excel.Application”))

print(“Message ” “1” “: ” “x = %gn”, x);

标准库中增加的 const fn

参数:
Safearray 安全数组类型的对象

 

取而代之的是,其他板条箱需要通过添加通配符来解决更多变体的可能性,例如_

(vlax-write-enabled-p object)

 

幕后发生的事情是,#[non_exhaustive] struct或的构造函数的可见性enum降低到pub(crate),从而阻止了在定义它的板条箱外部进行访问。

(vlax-safearray-get-u-bound safearray
dim) 
返回指定数组维度的上界(整数值)。如果指定的参数不是数组,将返回ActiveX错误。你需要将对该函数的调用封装到错误捕获内以使这个错误能得到适当处理。

#define WRONG(X, …, Y) #X #_ _VA_ARGS_ _
#y(这个是错误的例子。)

有关该#[non_exhaustive]属性的更多详细信息,可参见稳定性报告。

参数:

Listing 1 uses this and another macro using ## to do a bit of token
gluing.

PRINT_XN(1);        // becomes printf(“x1 = %dn”, x1);

(vlax-for symbol collection [expression1 [expression2]]…) 
循环集合的成员对象并对每一成员对象执行语句。如果第二个参数不是集合对象,将生成错误。引用的symbol是局部的和临时的,就如(foreach)一样。

{

图3-2显示了Documents集合对象的属性和方法。你可看到输出的第一行显示了带有一个描述的内部对象参照(IAcadDocuments),并且列出了可用的属性和方法。

int XNAME(1) = 14; // becomes int x1 = 14;

数据类型

2.Variadic Macros: … and _ _VA_ARGS_ _

AutoLISP提供了大量强大的映射和循环函数,如(while)、(foreach)、(mapcar)和(apply)。而Visual
LISP也增加了一些更适合用于与ActiveX集合对象的函数。这些函数包括有(vlax-for)、(vl-every)和(vlax-map-collection)等等。

PRINT_XN(2);        // becomes printf(“x2 = %dn”, x2);

(vlax-safearray-type symbol) 
如果symbol包含安全数组,元素的数据类型将以枚举结果返回(整数值)。这将可匹配整数或枚举结果(查看附录X有关数据类型枚举)。如果symbol不包含安全数组,将产生错误。

#、##和__VA_ARGS__  

 

1.#
假如希望在字符串中包含宏参数,ANSI
C允许这样作,在类函数宏的替换部分,#符号用作一个预处理运算符,它可以把语言符号转化程字符串。例如,如果x是一个宏参量,那么#x可以把参数名转化成相应的字符串。该过程称为字符串化(stringizing).
#incldue <stdio.h>
#define PSQR(x) printf(“the square of” #x “is %d.n”,(x)*(x))
int main(void)
{
    int y =4;
    PSQR(y);
    PSQR(2+4);
    return 0;
}
输出结果:
the square of y is 16.
the square of 2+4 is 36.
第一次调用宏时使用“y”代替#x;第二次调用时用“2+4″代#x。
2.##
##运算符可以用于类函数宏的替换部分。另外,##还可以用于类对象宏的替换部分。这个运算符把两个语言符号组合成单个语言符号。例如:
#define XNAME(n) x##n
这样宏调用:
XNAME(4)
展开后:
x4
程序:
#include <stdio.h>
#define XNAME(n) x##n
#define PXN(n) printf(“x”#n” = %dn”,x##n)
int main(void)
{
    int XNAME(1)=12;//int x1=12;
    PXN(1);//printf(“x1 = %dn”, x1);
    return 0;
}
输出结果:
x1=12
3.可变参数宏 …和_ _VA_ARGS_ _
__VA_ARGS__
是一个可变参数的宏,很少人知道这个宏,这个可变参数的宏是新的C99规范中新增的,目前似乎只有gcc支持(VC6.0的编译器不支持)。
实现思想就是宏定义中参数列表的最后一个参数为省略号(也就是三个点)。这样预定义宏_
_VA_ARGS_
_就可以被用在替换部分中,替换省略号所代表的字符串。比如:
#define PR(…) printf(__VA_ARGS__)
int main()
{
    int wt=1,sp=2;
    PR(“hellon”);
    PR(“weight = %d, shipping = %d”,wt,sp);
    return 0;
}
输出结果:
hello
weight = 1, shipping = 2
省略号只能代替最后面的宏参数。
#define W(x,…,y)错误!

 

 

 

 

 

 

 

 

较大的项目都会用大量的宏定义来组织代码,你可以看看/usr/include下面的头文件中用
了多少个宏定义。看起来宏展开就是做个替换而已,其实里面有比较复杂的规则,C语言有很多复杂但不常用的语法规则本书并不涉及,但有关宏展开的语法规则本
节却力图做全面讲解,因为它很重要也很常用。 
2.1. 函数式宏定义 
以前我们用过的#define N 20或#define STR “hello,
world”这种宏定义可以称为变量式宏定义(Object-like
Macro),宏定义名可以像变量一样在代码中使用。另外一种宏定义可以像函数调用一样在代码中使用,称为函数式宏定义(Function-like
Macro)。例如编辑一个文件main.c: 

 

#define MAX(a, b) ((a)>(b)?(a):(b))
k = MAX(i&0x0f, j&0x0f)

我们想看第二行的表达式展开成什么样,可以用gcc的-E选项或cpp命令,尽管这个C程序不合语法,但没关系,我们只做预处理而不编译,不会检查程序是否符合C语法。 

 

永利官网ylg客户端 8

$ cpp main.c
# 1 "main.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "main.c"

k = ((i&0x0f)>(j&0x0f)?(i&0x0f):(j&0x0f))

永利官网ylg客户端 9

 

就像函数调用一样,把两个实参分别替换到宏定义中形参a和b的位置。注意这种函数式宏定义和真正的函数调用有什么不同: 
1、函数式宏定义的参数没有类型,预处理器只负责做形式上的替换,而不做参数类型检查,所以传参时要格外小心。 
2、调用真正函数的代码和调用函数式宏定义的代码编译生成的指令不同。如果MAX是个真正的函数,那么它的函数体return
a > b ? a :
b;要编译生成指令,代码中出现的每次调用也要编译生成传参指令和call指令。而如果MAX是个函数式宏定义,这个宏定义本身倒不必编译生成指令,但是
代码中出现的每次调用编译生成的指令都相当于一个函数体,而不是简单的几条传参指令和call指令。所以,使用函数式宏定义编译生成的目标文件会比较大。 
3、定义这种宏要格外小心,如果上面的定义写成#define MAX(a, b)
(a>b?a:b),省去内层括号,则宏展开就成了k =
(i&0x0f>j&0x0f?i&0x0f:j&0x0f),运算的优先级就错了。同样道理,这个宏定义的外层
括号也是不能省的,想一想为什么。 
4、调用函数时先求实参表达式的值再传给形参,如果实参表达式有Side
Effect,那么这些Side Effect只发生一次。例如MAX(++a,
++b),如果MAX是个真正的函数,a和b只增加一次。但如果MAX是上面那样的宏定义,则要展开成k
= ((++a)>(++b)?(++a):(++b)),a和b就不一定是增加一次还是两次了。 
5、即使实参没有Side
Effect,使用函数式宏定义也往往会导致较低的代码执行效率。下面举一个极端的例子,也是个很有意思的例子。 
例 21.1. 函数式宏定义 

 

永利官网ylg客户端 10

#define MAX(a, b) ((a)>(b)?(a):(b))

int a[] = { 9, 3, 5, 2, 1, 0, 8, 7, 6, 4 };

int max(int n)
{
    return n == 0 ? a[0] : MAX(a[n], max(n-1));
}

int main(void)
{
    max(9);
    return 0;
}

永利官网ylg客户端 11

 

这段代码从一个数组中找出最大的数,如果MAX是个真正的函数,这个算法就是从前到后遍历一遍数组,时间复杂度是Θ(n),而现在MAX是这样一个函数式宏定义,思考一下这个算法的时间复杂度是多少? 
尽管函数式宏定义和真正的函数相比有很多缺点,但只要小心使用还是会显著提高代码的执行效率,毕竟省去了分配和释放栈帧、传参、传返回值等一系列工作,因
此那些简短并且被频繁调用的函数经常用函数式宏定义来代替实现。例如C标准库的很多函数都提供两种实现,一种是真正的函数实现,一种是宏定义实现,这一点
以后还要详细解释。 
函数式宏定义经常写成这样的形式(取自内核代码include/linux/pm.h): 

 

#define device_init_wakeup(dev,val) 
        do { 
                device_can_wakeup(dev) = !!(val); 
                device_set_wakeup_enable(dev,val); 
        } while(0)

 

为什么要用do { … } while(0)括起来呢?不括起来会有什么问题呢? 

 

永利官网ylg客户端 12

#define device_init_wakeup(dev,val) 
                device_can_wakeup(dev) = !!(val); 
                device_set_wakeup_enable(dev,val);

if (n > 0)
    device_init_wakeup(d, v);

永利官网ylg客户端 13

 

这样宏展开之后,函数体的第二条语句不在if条件中。那么简单地用{ …
}括起来组成一个语句块不行吗? 

 

永利官网ylg客户端 14

#define device_init_wakeup(dev,val) 
                { device_can_wakeup(dev) = !!(val); 
                device_set_wakeup_enable(dev,val); }

if (n > 0)
    device_init_wakeup(d, v);
else
    continue;

永利官网ylg客户端 15

 

问题出在device_init_wakeup(d,
v);末尾的;号,如果不允许写这个;号,看起来不像个函数调用,可如果写了这个;号,宏展开之后就有语法错误,if语句被这个;号结束掉了,没法跟
else配对。因此,do { … } while(0)是一种比较好的解决办法。 
如果在一个程序文件中重复定义一个宏,C语言规定这些重复的宏定义必须一模一样。例如这样的重复定义是允许的: 

 

#define OBJ_LIKE (1 - 1)
#define OBJ_LIKE /* comment */ (1/* comment */-/* comment */  1)/* comment */

 

在定义的前后多些空白(这里的空白包括空格、Tab、注释,因为前一步预处理要把注释替换成空格)没有关系,在定义中间连续多个空白等价于一个空白,但在定义中间有空白和没有空白被认为是不同的,所以这样的重复定义是不允许的: 

 

#define OBJ_LIKE (1 - 1)
#define OBJ_LIKE (1-1)

 

如果需要重新定义一个宏,和原来的定义不同,可以先用#undef取消原来的定义,再重新定义,例如: 

 

永利官网ylg客户端 16

#define X 3
... /* X is 3 */
#undef X
... /* X has no definition */
#define X 2
... /* X is 2 */

永利官网ylg客户端 17

 

2.2. 内联函数  
C99引入一个新关键字inline,用于定义内联函数(inline
function)。这种用法在内核代码中很常见,例如include/linux/rwsem.h中: 

 

永利官网ylg客户端 18

static inline void down_read(struct rw_semaphore *sem)
{
        might_sleep();
        rwsemtrace(sem,"Entering down_read");
        __down_read(sem);
        rwsemtrace(sem,"Leaving down_read");
}

永利官网ylg客户端 19

 

inline关键字告诉编译器,这个函数的调用要尽可能快,可以当普通的函数调用实现,也可以用宏展开的办法实现。我们做个实验,把上一节的例子改一下: 
例 21.2. 内联函数 

 

永利官网ylg客户端 20

inline int MAX(int a, int b)
{
    return a > b ? a : b;
}

int a[] = { 9, 3, 5, 2, 1, 0, 8, 7, 6, 4 };

int max(int n)
{
    return n == 0 ? a[0] : MAX(a[n], max(n-1));
}

int main(void)
{
    max(9);
    return 0;
}

永利官网ylg客户端 21

 

按往常的步骤编译然后反汇编: 

 

永利官网ylg客户端 22

$ gcc main.c -g
$ objdump -dS a.out
...
int max(int n)
{
 8048369:       55                      push   %ebp
 804836a:       89 e5                   mov    %esp,%ebp
 804836c:       83 ec 0c                sub    $0xc,%esp
        return n == 0 ? a[0] : MAX(a[n], max(n-1));
 804836f:       83 7d 08 00             cmpl   $0x0,0x8(%ebp)
 8048373:       75 0a                   jne    804837f <max+0x16>
 8048375:       a1 c0 95 04 08          mov    0x80495c0,%eax
 804837a:       89 45 fc                mov    %eax,-0x4(%ebp)
 804837d:       eb 29                   jmp    80483a8 <max+0x3f>
 804837f:       8b 45 08                mov    0x8(%ebp),%eax
 8048382:       83 e8 01                sub    $0x1,%eax
 8048385:       89 04 24                mov    %eax,(%esp)
 8048388:       e8 dc ff ff ff          call   8048369 <max>
 804838d:       89 c2                   mov    %eax,%edx
 804838f:       8b 45 08                mov    0x8(%ebp),%eax
 8048392:       8b 04 85 c0 95 04 08    mov    0x80495c0(,%eax,4),%eax
 8048399:       89 54 24 04             mov    %edx,0x4(%esp)
 804839d:       89 04 24                mov    %eax,(%esp)
 80483a0:       e8 9f ff ff ff          call   8048344 <MAX>
 80483a5:       89 45 fc                mov    %eax,-0x4(%ebp)
 80483a8:       8b 45 fc                mov    -0x4(%ebp),%eax
}
...

永利官网ylg客户端 23

 

可以看到MAX是作为普通函数调用的。如果指定优化选项编译,然后反汇编: 

永利官网ylg客户端 24

$ gcc main.c -g -O
$ objdump -dS a.out
...
int max(int n)
{
 8048355:       55                      push   %ebp
 8048356:       89 e5                   mov    %esp,%ebp
 8048358:       53                      push   %ebx
 8048359:       83 ec 04                sub    $0x4,%esp
 804835c:       8b 5d 08                mov    0x8(%ebp),%ebx
        return n == 0 ? a[0] : MAX(a[n], max(n-1));
 804835f:       85 db                   test   %ebx,%ebx
 8048361:       75 07                   jne    804836a <max+0x15>
 8048363:       a1 a0 95 04 08          mov    0x80495a0,%eax
 8048368:       eb 18                   jmp    8048382 <max+0x2d>
 804836a:       8d 43 ff                lea    -0x1(%ebx),%eax
 804836d:       89 04 24                mov    %eax,(%esp)
 8048370:       e8 e0 ff ff ff          call   8048355 <max>
inline int MAX(int a, int b)
{
        return a > b ? a : b;
 8048375:       8b 14 9d a0 95 04 08    mov    0x80495a0(,%ebx,4),%edx
 804837c:       39 d0                   cmp    %edx,%eax
 804837e:       7d 02                   jge    8048382 <max+0x2d>
 8048380:       89 d0                   mov    %edx,%eax
int a[] = { 9, 3, 5, 2, 1, 0, 8, 7, 6, 4 };

int max(int n)
{
        return n == 0 ? a[0] : MAX(a[n], max(n-1));
}
 8048382:       83 c4 04                add    $0x4,%esp
 8048385:       5b                      pop    %ebx
 8048386:       5d                      pop    %ebp
 8048387:       c3                      ret    
...

永利官网ylg客户端 25

可以看到,并没有call指令调用MAX函数,MAX函数的指令是内联在max函数中的,由于源代码和指令的次序无法对应,max和MAX函数的源代码也交错在一起显示。 
2.3. #、##运算符和可变参数  
在函数式宏定义中,#运算符用于创建字符串,#运算符后面应该跟一个形参(中间可以有空格或Tab),例如: 

 

#define STR(s) # s
STR(hello     world)

 

用cpp命令预处理之后是”hello?world”,自动用”号把实参括起来成为一个字符串,并且实参中的连续多个空白字符被替换成一个空格。 
再比如: 

 

#define STR(s) #s
fputs(STR(strncmp("ab"cd", "abc", '4"')
    == 0) STR(: @n), s);

预处理之后是fputs(“strncmp(“ab”cd”, “abc”, ‘4″‘) == 0″ “: @n”,
s);,注意如果实参中包含字符常量或字符串,则宏展开之后字符串的界定符”要替换成”,字符常量或字符串中的和”字符要替换成和”。 
在宏定义中可以用##运算符把前后两个预处理Token连接成一个预处理Token,和#运算符不同,##运算符不仅限于函数式宏定义,变量式宏定义也可以用。例如: 

 

#define CONCAT(a, b) a##b
CONCAT(con, cat)

 

预处理之后是concat。再比如,要定义一个宏展开成两个#号,可以这样定义: 

 

#define HASH_HASH # ## #

 

中间的##是运算符,宏展开时前后两个#号被这个运算符连接在一起。注意中间的两个空格是不可少的,如果写成####,会被划分成##和##两个Token,而根据定义##运算符用于连接前后两个预处理Token,不能出现在宏定义的开头或末尾,所以会报错。 
我们知道printf函数带有可变参数,函数式宏定义也可以带可变参数,同样是在参数列表中用…表示可变参数。例如: 

 

#define showlist(...) printf(#__VA_ARGS__)
#define report(test, ...) ((test)?printf(#test):
    printf(__VA_ARGS__))
showlist(The first, second, and third items.);
report(x>y, "x is %d but y is %d", x, y);

预处理之后变成:

printf("The first, second, and third items.");
((x>y)?printf("x>y"): printf("x is %d but y is %d", x, y));

在宏定义中,可变参数的部分用__VA_ARGS__表示,实参中对应…的几个参数可以看成一个参数替换到宏定义中__VA_ARGS__所在的地方。 
调用函数式宏定义允许传空参数,这一点和函数调用不同,通过下面几个例子理解空参数的用法。 

#define FOO() foo
FOO()

预处理之后变成foo。FOO在定义时不带参数,在调用时也不允许传参数给它。 

 

#define FOO(a) foo##a
FOO(bar)
FOO()

 

预处理之后变成: 

 

foobar
foo

 

FOO在定义时带一个参数,在调用时必须传一个参数给它,如果不传参数则表示传了一个空参数。 

 

#define FOO(a, b, c) a##b##c
FOO(1,2,3)
FOO(1,2,)
FOO(1,,3)
FOO(,,3)

 

预处理之后变成: 

 

123
12
13
3

 

FOO在定义时带三个参数,在调用时也必须传三个参数给它,空参数的位置可以空着,但必须给够三个参数,FOO(1,2)这样的调用是错误的。 

#define FOO(a, ...) a##__VA_ARGS__
FOO(1)
FOO(1,2,3,)

 预处理之后变成: 

 

1
12,3,

FOO(1)这个调用相当于可变参数部分传了一个空参数,FOO(1,2,3,)这个调用相当于可变参数部分传了三个参数,第三个是空参数。 
gcc有一种扩展语法,如果##运算符用在__VA_ARGS__前面,除了起连接作用之外还有特殊的含义,例如内核代码net/netfilter/nf_conntrack_proto_sctp.c中的: 

 

#define DEBUGP(format, ...) printk(format, ## __VA_ARGS__)

 

printk这个内核函数相当于printf,也带有格式化字符串和可变参数,由于内核不能调用libc的函数,所以另外实现了一个打印函数。这个
函数式宏定义可以这样调用:DEBUGP(“info no. %d”,
1)。也可以这样调用:DEBUGP(“info”)。后者相当于可变参数部分传了一个空参数,但展开后并不是printk(“info”,),而是
printk(“info”),当__VA_ARGS是空参数时,##运算符把它前面的,号“吃”掉了。 
2.4. 宏展开的步骤  
以上举的宏展开的例子都是最简单的,有些宏展开的过程要做多次替换,例如: 

 

#define sh(x) printf("n" #x "=%d, or %dn",n##x,alt[x])
#define sub_z  26
sh(sub_z)

 

sh(sub_z)要用sh(x)这个宏定义来展开,形参x对应的实参是sub_z,替换过程如下: 

  1. #x要替换成”sub_z”。 
  2. n##x要替换成nsub_z。 
    3.
    除了带#和##运算符的参数之外,其它参数在替换之前要对实参本身做充分的展开,所以应该先把sub_z展开成26再替换到alt[x]中x的位置。 
  3. 现在展开成了printf(“n” “sub_z” “=%d, or
    %dn”,nsub_z,alt[26]),所有参数都替换完了,这时编译器会再扫描一遍,再找出可以展开的宏定义来展开,假设nsub_z或alt是变量式宏定义,这时会进一步展开。 
    再举一个例子: 

 

永利官网ylg客户端 26

#define x 3
#define f(a) f(x * (a))
#undef x
#define x 2
#define g f
#define t(a) a

t(t(g)(0) + t)(1);

永利官网ylg客户端 27

 

展开的步骤是: 

  1. 先把g展开成f再替换到#define t(a) a中,得到t(f(0) + t)(1);。 
  2. 根据#define f(a) f(x * (a)),得到t(f(x * (0)) + t)(1);。 
  3. 把x替换成2,得到t(f(2 * (0)) +
    t)(1);。注意,一开始定义x为3,但是后来用#undef
    x取消了x的定义,又重新定义x为2。当处理到t(t(g)(0) +
    t)(1);这一行代码时x已经定义成2了,所以用2来替换。还要注意一点,现在得到的t(f(2
    * (0)) + t)(1);中仍然有f,但不能再次根据#define f(a) f(x *
    (a))展开了,f(2 *
    (0))就是由展开f(0)得到的,这里面再遇到f就不展开了,这样规定可以避免无穷展开(类似于无穷递归),因此我们可以放心地使用递归定义,例
    如#define a a[0],#define a a.member等。 
  4. 根据#define t(a) a,最终展开成f(2 * (0)) +
    t(1);。这时不能再展开t(1)了,因为这里的t就是由展开t(f(2 * (0)) +
    t)得到的,所以不能再展开了。

 

 

 

 

可变参数宏

 

在 GNU C 中,宏可以接受可变数目的参数,就象函数一样,例如:
#define pr_debug(fmt,arg…)
printk(KERN_DEBUG fmt,##arg)

用可变参数宏(variadic macros)传递可变参数表
你可能很熟悉在函数中使用可变参数表,如:

void printf(const char* format, …);

直到最近,可变参数表还是只能应用在真正的函数中,不能使用在宏中。

C99编译器标准终于改变了这种局面,它允许你可以定义可变参数宏(variadic
macros),这样你就可以使用拥有可以变化的参数表的宏。可变参数宏就像下面这个样子:

#define debug(…) printf(__VA_ARGS__)

缺省号代表一个可以变化的参数表。使用保留名 __VA_ARGS__
把参数传递给宏。当宏的调用展开时,实际的参数就传递给 printf()了。例如:

Debug(“Y = %dn”, y);

而处理器会把宏的调用替换成:

printf(“Y = %dn”, y);

因为debug()是一个可变参数宏,你能在每一次调用中传递不同数目的参数:

debug(“test”); //一个参数

可变参数宏不被ANSI/ISO C++
所正式支持。因此,你应当检查你的编译器,看它是否支持这项技术。

 

使用图3-2的例子,Item方法可通过以下任何方法使用:

{

更多关于变体和安全数组的内容,请参阅第六章。

For example, you could do this:

 

#define XNAME(n) x ## n

示例:
(vl-filename-base “c:\myfiles\drawing1.dwg”) 
返回“drawing1”
(vl-filename-base “drawing1.dwg”) 
返回“drawing1”

    PR(2, “x = %.2f, y = %.4fn”, x, y);

(vl-file-directory-p filename) 
如果filename是目录文件夹名称则返回T,如果filename实际上是一个文件或根本不存在,则返回nil。

int XNAME(2) = 20; // becomes int x2 = 20;

 

用GCC和C99的可变参数宏, 更方便地打印调试信息

gcc的预处理提供的可变参数宏定义真是好用: 

#ifdef DEBUG
 #define dbgprint(format,args...) 
   fprintf(stderr, format, ##args)
#else
    #define dbgprint(format,args...)
#endif

如此定义之后,代码中就可以用dbgprint了,例如dbgprint(“aaa %s”, __FILE__);。感觉这个功能比较Cool  :em11: 

下面是C99的方法: 

#define dgbmsg(fmt,...) 
             printf(fmt,__VA_ARGS__)

新的C99规范支持了可变参数的宏

具体使用如下:

 以下内容为程序代码:

 #include <stdarg.h> #include <stdio.h>

 #define LOGSTRINGS(fm, …) printf(fm,__VA_ARGS__)

 int main() {      LOGSTRINGS(“hello, %d “, 10);      return 0; } 

 但现在似乎只有gcc才支持。

提示!类型库有很多种形式,他们通常是.TLB文件。但也可以是.OLB、.DLL,甚至是.EXE。值得一提的是Microsoft
Office 97 和 2000版通常用.TLB文件,而OFFICE
XP自己使用.EXE文件来对类型库提供对其它应用的接口定义。请翻阅与你准备使用外部应用程序或服务相关的参考资料以了解它是如何暴露共ActiveX类型库信息的内容。

printf(“Howdy”);

(vlax-get-object program-id)

上面的宏是使用qDebug输出调试信息,在非Qt的程序中也可以改为printf,守护进程则可以改为syslog等等…
其中,决窍其实就是这几个宏 ##__VA_ARGS__, __FILE__,
__LINE__ 和__FUNCTION__,下面介绍一下这几个宏:
  1) __VA_ARGS__
是一个可变参数的宏,很少人知道这个宏,这个可变参数的宏是新的C99规范中新增的,目前似乎只有gcc支持(VC6.0的编译器不支持)。宏前面加上##的作用在于,当可变参数的个数为0时,这里的##起到把前面多余的”,”去掉的作用,否则会编译出错,
你可以试试。
  2) __FILE__ 宏在预编译时会替换成当前的源文件名
  3) __LINE__宏在预编译时会替换成当前的行号
  4) __FUNCTION__宏在预编译时会替换成当前的函数名称

不幸的是,没有直接的方法获取给定对象的所有属性的列表来用于遍历编程。不过,你还是可以获取列表信息的,这样对你的帮助也不算小。

Listing 2 shows a slightly more ambitious example that uses string
concatenation and the # operator:

(vl-directory-files path pattern [mode]) 
按照不同的模式,返回文件或子文件夹的列表。

x2 = 20

示例:
(vl-file-copy “c:\myfile1.txt”
“c:\mycopy.txt”) 
(vl-file-copy “c:\myfile2.txt” “c:\mycopy.txt” T);
添加目录文件 

##将两个符号连接成一个。

表 6-1 – Visual LISP默认LISP->变体数据类型映射 

Then the macro

参数:

// variadic.c — variadic macros

方法是对象提供的用于访问或编辑专用特性或针对对象本身执行特别动作的内置函数。常见的方法有Rotate(旋转)、Erase(删除)、Copy(复制)、Scale(比例缩放)和Offset(偏移)。你可能注意到,这些看起来就像AutoCAD编辑命令。嗯,在本质上是这样的,但略有不同。

    y = sqrt(x);

可能你喜欢在列表框中显示图纸文件清单,而不显示其扩展名(这样的名称会短一些)。就可结合目录列表框和vl_filename-base函数来实现,如下所示:
(mapcar ‘vl-filename-base (vl-directory-files
pathname “*.dwg”)) 

Suppose you later invoke the macro like this:

安全数组相当于AutoLISP中的LIST对象。主要的区别在于它是静态的。意思就是说他不可能根据他们能存储的数字的多少来加长或改变项数。这将避免了在试图分配或取得超出数组长度的元素时产生的不必要的错误。这就是它们之所以被称为“安全”的原因。任何传递到ActiveX的LIST结构必须先转换成安全数组。任何从ActiveX对象获得的LIST面向对象的数据都必须通过象(car)、(nth)、(accoc)、(mapcar)、(member)等的LISP函数转换成LIST数据类型。Visual
LISP为创造操作、阅读安全数组数据值提供了大量的功能。

    double x = 48;

(vl-load-com) 
(vl-doc-export ‘example1) 
(defun example1 () 
   (princ “这是一个透明函数的示例。”)
   (princ) 
)   
(vlax-add-cmd “example1” ‘example1 “example1” ACRX_CMD_TRANSPARENT) 
(princ) 
命令: LINE 
指定第一点: ‘EXAMPLE1 
这是一个透明函数的示例。 
重新回到LINE命令。 
指定第一点:

 

参数:
RegKey 在HKEY_LOCAL_MACHINE或HKEY_CURRENT_USER单元中的注册表项名称。
Value-Name (可选项)在指定注册表项下方的从属值名(符号)的名称。

x1 = 14

(vl-position item list) 
找到时返回在list中item的第n个位置。如果list中未找到item,则返回nil。第一元素的位置索引为零(0)。

x4

(vl-every predicate-function list…)

print(“Message 1: x = %gn”, x);

然而一般的AutoCAD编辑命令,必须在每步执行中验证对象。而方法是由主对象提供的,因此,只能由每一对象单独提供支持的方法。晕了吧?

 

Object – 任何代表图元对象类型的vla-对象。

#include <stdio.h>

(vlax-safearray->list
(vlax-variant-value vartest)) 
返回结果是(0.0 0.0 0.0)列表值。

“weight = %d, shipping = $%.2fn”, wt, sp

参数:
Path 字符串,指要查询的路径名。
Pattern 字符串,指要查询文件。可以包含通配符。如果不指定或nil,则使用“*.*”
Mode (可选)整数。以下其中一个…
-1 = 只列出目录名
0 =  列出文件及目录(未指定时的默认值)
1 =  只列出文件

}

Visual LISP变体函数
(vlax-make-variant [value] [type]) 
使用给定的值或符号赋值来创建一个变体对象

Message 1: x = 48

 

 

成功时返回外部Excel应用程序进程的vla-对象,否则返回nil。

Thus, the resulting code is this:

(setq vartest (vlax-make-variant “dog”
vlax-vbString)) 
(vlax-variant-type vartest) 
返回 8 (字符串类型) 

Some functions, such as printf(), accept a variable number of arguments.
The stdvar.h header file,provides tools for creating user-defined
functions with a variable number of arguments. And C99 does the same
thing for macros.Although not used in the standard, the word variadic
has come into currency to label this facility. (However, the process
that has added stringizing and variadic to the C vocabulary has not yet
led to labeling functions or macros with a fixed number of arguments as
fixadic functions and normadic macros.)

示例:
(setq vartest (vlax-make-variant 6
vlax-vbInteger)) 
(vlax-variant-type vartest) 
返回 2 (整数类型) 

PR(“Howdy”);

在AutoCAD中的一些公共的词典有PageSetups (页面设置)、Layouts
(布局)(它同样也做为词典保存),还有Express Tools中的个别组件,如WipeOuts
(遮罩)。Xrecord对象也保存在词典内。

}

Visual
LISP需要类型库信息来确定对象的方法、属性和常量是否存在。有些对象不含有任何类型库信息,例如AcadDocument对象。

Here’s the output:

发表评论

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