Menu
Woocommerce Menu

具体更新内容如下,我将尝试给一些基本的面向对象方面的描述

0 Comment


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

在本章中,我们将更多讨论有关在Visual
LISP中使用ActiveX性能。首先,我们从ActiveX的技术环境开始,包括对象、对象模型、集合、属性、方法等方面。然后我们将深挖一些特定的ActiveX技术部分。了解ActiveX功能性是学习任何语言必不可少的工作。

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

1.Preprocessor Glue: The ## Operator

Scala
Macros对scala函数库编程人员来说是一项不可或缺的编程工具,可以通过它来解决一些用普通编程或者类层次编程(type
level programming)都无法解决的问题,这是因为Scala
Macros可以直接对程序进行修改。Scala
Macros的工作原理是在程序编译时按照编程人员的意旨对一段程序进行修改产生出一段新的程序。具体过程是:当编译器在对程序进行类型验证(typecheck)时如果发现Macro标记就会将这个Macro的功能实现程序(implementation):一个语法树(AST,
Abstract Syntax
Tree)结构拉过来在Macro的位置进行替代,然后从这个AST开始继续进行类型验证过程。

具体更新内容如下:

ActiveX是一种面向对象的工具,也就是说它表现为对象的使用以及对象间的关联这种方式。在这里我不想去解释面向对象的问题,这些东西最好留给那些更专业的教科书去讲。然而,我将尝试给一些基本的面向对象方面的描述,以便大家有一个基本的了解。

函数属性(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

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

下面我们先用个简单的例子来示范分析一下Def
Macros的基本原理和使用方法:

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

类型属性(Type Attributes)

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

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. 

1 object modules {2    greeting("john")3  }4  5  object mmacros {6    def greeting(person: String): Unit = macro greetingMacro7    def greetingMacro(c: Context)(person: c.Expr[String]): c.Expr[Unit] = ...8  }

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

在面向对象的环境中,任何事物都是以Classes(类)开始的。类是抽象的框架,用于描述对象应是什么样的,以及他们应该如何表现和相互联系的。在某种意义上说类定义了对象类型的种类。例如,一辆汽车是属于交通工具类。交通工具是父类,而汽车是子类。反之,你可取得更多特定的和额外定义的子类,例如旅行车、面包车和运动型轿车。

变量属性(Variable Attribute)

  • aligned
  • packed

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

以上是Def
Macros的标准实现模式。基本原理是这样的:当编译器在编译modules遇到方法调用greeting时会进行函数符号解析、在mmacros里发现greeting是个macro,它的具体实现在greetingMacro函数里,此时编译器会运行greetingMacro函数并将运算结果-一个AST调用替代表达式greeting。注意编译器在运算greetingMacro时会以AST方式将参数person传入。由于在编译modules对象时需要运算greetingMacro函数,所以greetingMacro函数乃至整个mmacros对象必须是已编译状态,这就意味着modules和mmacros必须分别在不同的源代码文件里,而且还要确保在编译modules前先完成对mmacros的编译,我们可以从sbt设置文件build.sbt看到它们的关系:

// 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...

类不处理特定的实例,他们更注重于实例方面的描述,而非使用方面。当你使用类时,你可以说调用类的一个实例。调用类的结果经常是创建了一个对象。对象可以是单独的图元或包含了更多对象的容器。

Clang特有的

  • availability
  • overloadable

For example, you could do this:

 1 name := "learn-macro" 2  3 version := "1.0.1" 4  5 val commonSettings = Seq( 6   scalaVersion := "2.11.8", 7   scalacOptions ++= Seq("-deprecation", "-feature"), 8   libraryDependencies ++= Seq( 9     "org.scala-lang" % "scala-reflect" % scalaVersion.value,10     "org.scala-lang.modules" %% "scala-parser-combinators" % "1.0.1",11     "org.specs2" %% "specs2" % "2.3.12" % "test",12     "org.scalatest" % "scalatest_2.11" % "2.2.1" % "test"13   ),14   addCompilerPlugin("org.scalamacros" % "paradise" % "2.1.0" cross CrossVersion.full)15 )16 17 lazy val root = (project in file(".")).aggregate(macros, demos)18 19 lazy val macros = project.in(file("macros")).settings(commonSettings : _*)20 21 lazy val demos  = project.in(file("demos")).settings(commonSettings : _*).dependsOn

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

对象
一个对象就是类的一个实例。对象有其固定的属性,同时也可以有固定的方法和事件。属性是定义对象怎样表现和反应的特性。方法是用于访问或修改对象属性或某些行为的内置函数。事件是由对象发送的通知,当执行或激活它们时,它用于响应特定动作。

书写格式

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

__attribute__(xxx)

#define XNAME(n) x ## n

注意最后一行:demos
dependsOn,因为我们会把所有macros定义文件放在macros目录下。

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

就用上面的汽车类的例子来说,对象可以是特别的轿车。比如说,你的轿车,带有特别的配置(制造、型号、颜色、选配、以及序列号)。你可以说,你的轿车是汽车类的一个实例,或汽车类派生的一些类。

常见的系统用法

Then the macro

下面我们来看看macro的具体实现方法:

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

图3-1 对象模型

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”指定第一个可变参数所在的索引.

XNAME(4)

 1 import scala.language.experimental.macros 2 import scala.reflect.macros.blackbox.Context 3 import java.util.Date 4 object LibraryMacros { 5   def greeting(person: String): Unit = macro greetingMacro 6  7   def greetingMacro(c: Context)(person: c.Expr[String]): c.Expr[Unit] = { 8     import c.universe._ 9     println("compiling greeting ...")10     val now = reify {new Date().toString}11     reify {12       println("Hello " + person.splice + ", the time is: " + new Date().toString)13     }14   }15 }

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

对象模型
对象模型是一个随意的架构,或类之间的分层定义的排列关系,意思就是可以从更高级别的类来获得一个对象。对象模型与访问它的语言或工具无关,它有自己的逻辑框架。不管你是使用Visual
Basic、VBA、Visual
LISP、Delphi、Java、C/C++、C#.NET或带有ActiveX接口的其它语言,它都将以相同的模型存在。这并不代表对象模型的所有特性都支持所有语言。某些特性在某些语言中可以访问或比较容易被访问,但在其它语言中可能就不行。

noreturn

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

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

would expand to the following:

以上是macro
greeting的具体声明和实现。代码放在macros目录下的MacrosLibrary.scala里。首先必须import
macros和Context。

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 */
    }
}

我们将对象模型比喻成一套房子,它由房间、门和窗组成。不同的人都可进入和使用房子,而他们都是面对同样的这套房子。在这种情况下,房子和房间就是对象模型,而人即是编程语言。这样,你应该懂吧。

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(@"我是新方法");
}

效果:

图片 1

Paste_Image.png

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

图片 2

Paste_Image.png

x4

macro调用在demo目录下的HelloMacro.scala里:

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

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

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

图片 3

Paste_Image.png

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

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

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

1 object HelloMacro extends App {2   import LibraryMacros._3   greeting("john")4 }
match ordering {
    Relaxed | Release | Acquire | AcqRel | SeqCst => { /* ... */ }
    // OK; if more variants are added, nothing will break.
    _ => { /* logic */ }
}

你可向下层浏览对象模型进入下一层次的对象和集合,也可以向上层浏览父对象和集合。模型非常强大,应用程序可直接访问和操作环境来执行几乎无限的任务。它同时保持每一事物都整齐有序,在开发软件解决方案时能提供有效的帮助。

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;
}

 

注意在编译HelloMacro.scala时产生的输出:

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

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

NSObject

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

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

// glue.c — use the ## operator

Mac-Pro:learn-macro tiger-macpro$ sbt[info] Loading global plugins from /Users/tiger-macpro/.sbt/0.13/plugins[info] Loading project definition from /Users/tiger-macpro/Scala/IntelliJ/learn-macro/project[info] Set current project to learn-macro (in build file:/Users/tiger-macpro/Scala/IntelliJ/learn-macro/)> project demos[info] Set current project to demos (in build file:/Users/tiger-macpro/Scala/IntelliJ/learn-macro/)> compile[info] Compiling 1 Scala source to /Users/tiger-macpro/Scala/IntelliJ/learn-macro/macros/target/scala-2.11/classes...[info] 'compiler-interface' not yet compiled for Scala 2.11.8. Compiling...[info]   Compilation completed in 7.876 s[info] Compiling 1 Scala source to /Users/tiger-macpro/Scala/IntelliJ/learn-macro/demos/target/scala-2.11/classes...compiling greeting ...[success] Total time: 10 s, completed 2016-11-9 9:28:24> 

Macro and attribute 的改进

在AutoCAD中的一些公共的集合有Documents(文档)、Layers(图层)、Dimension
Styles(标注样式)、Linetypes(线型)、 Blocks(块)等等。

objc_designated_initializer

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

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

#include <stdio.h>

从compiling greeting
…这条提示我们可以得出在编译demo目录下源代码文件的过程中应该运算了greetingMacro函数。测试运行后产生结果:

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

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

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

#define XNAME(n) x ## n

Hello john, the time is: Wed Nov 09 09:32:04 HKT 2016Process finished with exit code 0

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

属性、方法和事件
属性只是描述关联于对象或集合的特性。例如它可包含名称、高度、宽度、旋转角度、比例缩放、颜色、图层、线型等等。属性根据不同的对象的类型而有所区别,但有些属性对所有对象和集合是通用的。集合和词典通常提供了Count和Name属性,还有Item和Add方法。只有词典提供了Delete方法,而你却不能通过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 PRINT_XN(n) printf(“x” #n ” = %d\n”, x ## n);

运算greeting实际上是调用了greetingMacro中的macro实现代码。上面这个例子使用了最基础的Scala
Macro编程模式。注意这个例子里函数greetingMacro的参数c:
Context和在函数内部代码中reify,splice的调用:由于Context是个动态函数接口,每个实例都有所不同。对于大型的macro实现函数,可能会调用到其它同样会使用到Context的辅助函数(helper
function),容易出现Context实例不匹配问题。另外reify和splice可以说是最原始的AST操作函数。我们在下面这个例子里使用了最新的模式和方法:

  • Macros
    in extern { ... } blocks.

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

常见用法

int main(void)

 1   def tell(person: String): Unit = macro MacrosImpls.tellMacro 2   class MacrosImpls(val c: Context) { 3     import c.universe._ 4       def tellMacro(person: c.Tree): c.Tree = { 5       println("compiling tell ...") 6       val now = new Date().toString 7       q""" 8           println("Hello "+$person+", it is: "+$now) 9         """10     }11   }

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

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

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

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

{

在这个例子里我们把macro实现函数放入一个以Context为参数的class。我们可以把所有使用Context的函数都摆在这个class里面大家共用统一的Context实例。quasiquotes是最新的AST操作函数集,可以更方便灵活地控制AST的产生、表达式还原等。这个tell
macro的调用还是一样的:

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

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

另一种方式,OFFSET(偏移)命令可以在任何时间使用,但如果你试图偏移一个文本对象,
AutoCAD就会出现出错信息。然而,文本对象本身提供的各种方法,如Copy(复制)、Rotate(旋转)、Scale(比例缩放)和Move(移动),但没有Offset(偏移)。所以你可以从对象“调用”各种方法,却需要确定它对使用的对象是有效的。

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字节对齐,这个常用于与协议有关的网络传输中.

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

1 object HelloMacro extends App {2   import LibraryMacros._3   greeting("john")4   tell("mary")5 }

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

事件是对象或集合由各种可以被检测到的或可以响应的活动所产生动作。当事件与这些事件的反应相结合使用时,就称为事件驱动编程。AutoCAD提供了一个被称之为反应器的强大的事件反应工具集,它使你可在绘图环境中发送触发器来响应各种事件。例如,当对象在活动的图形中被删除时,您可以创建一个反应器响应一个Erase事件。这只是一个事件和反应器的例子。

noinline & always_inline

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

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

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

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

使用例子:

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

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

测试运算产生下面的结果:

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

属性关联
有一点很重要,就是你决不需要知道哪些属性适用于哪些对象和集合。有两个特别的函数对于保证您的代码能够在运行时妥善处理属性和方法的执行:(vlax-property-available-p)和(vlax-method-applicable-p)。这两个函数只是两个Visual
LISP判断函数,它提供布尔测试判断条件是真或假(LISP术语为non-nil或nil)。

warn_unused_result

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

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

警告如下:

图片 4

Paste_Image.png

PRINT_XN(1);        // becomes printf(“x1 = %d\n”, x1);

Hello john, the time is: Wed Nov 09 11:42:21 HKT 2016Hello mary, it is: Wed Nov 09 11:42:20 HKT 2016Process finished with exit code 0
  • 在 procedural
    macros 中生成 macro_rules! items

这些函数的语法如下:
(vlax-property-available-p object property) 
(vlax-method-applicable-p object method) 

objc_subclassing_restricted

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

__attribute__((objc_subclassing_restricted))
@interface ViewController : UIViewController


@end

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

图片 5

Paste_Image.png

PRINT_XN(2);        // becomes printf(“x2 = %d\n”, x2);

Def
Macros的Macro实现函数可以是泛型函数,支持类参数。在下面的例子我们示范如何用Def
Macros来实现通用的case
class与Map类型的转换。假设我们有个转换器CaseClassMapConverter[C],那么C类型可以是任何case
class,所以这个转换器是泛型的,那么macro实现函数也就必须是泛型的了。大体来说,我们希望实现以下功能:把任何case
class转成Map:

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

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

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

警告如下:

图片 6

Paste_Image.png

    return 0;

 1  def ccToMap[C: CaseClassMapConverter]: Map[String,Any] = 2     implicitly[CaseClassMapConverter[C]].toMap 3  4   case class Person(name: String, age: Int) 5   case class Car(make: String, year: Int, manu: String) 6  7   val civic = Car("Civic",2016,"Honda") 8   println(ccToMap[Person](Person("john",18))) 9   println(ccToMap[Car]10 11 ...12 Map(name -> john, age -> 18)13 Map(make -> Civic, year -> 2016, manu -> Honda)
  • The $m:meta 匹配器支持 arbitrary
    token-stream
    values.

(if (setq ent (entsel
“\n选择对象以获取其属性: “))
  (progn
    (setq obj (vlax-ename->vla-object (car ent)))
    (princ
      (strcat “\n长度: ” (vla-get-Length obj))
    )
  )
)

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就具备快速打包功能了.

}

反向把Map转成case
class:

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

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

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("before\n");
}

讨论:+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函数.

Here’s the output:

 1   def mapTocc[C: CaseClassMapConverter](m: Map[String,Any]) = 2     implicitly[CaseClassMapConverter[C]].fromMap 3  4   val mapJohn = ccToMap[Person](Person("john",18)) 5   val mapCivic = ccToMap[Car] 6   println(mapTocc[Person] 7   println(mapTocc[Car]) 8  9 ...10 Person(john,18)11 Car(Civic,2016,Honda)
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 } );

(if (setq ent (entsel
“\n选择对象以获取其属性: “))
  (progn
    (setq obj (vlax-ename->vla-object (car ent)))
    (if (vlax-property-available-p obj ‘Length)
      (princ
        (strcat “\n长度: ” (vla-get-Length obj))
      )
      (princ “\n对象没有 LENGTH 属性…”)
    )
  )
)

enable_if

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

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

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

图片 7

Paste_Image.png

x1 = 14

我们来看看这个Macro的实现函数:macros/CaseClassConverter.scala

标准库中增加的 const fn

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

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的任意位置,防止忘记.

x2 = 20

 1 import scala.language.experimental.macros 2 import scala.reflect.macros.whitebox.Context 3  4 trait CaseClassMapConverter[C] { 5   def toMap: Map[String,Any] 6   def fromMap(m: Map[String,Any]): C 7 } 8 object CaseClassMapConverter { 9   implicit def Materializer[C]: CaseClassMapConverter[C] = macro converterMacro[C]10   def converterMacro[C: c.WeakTypeTag](c: Context): c.Tree = {11     import c.universe._12 13     val tpe = weakTypeOf[C]14     val fields = tpe.decls.collectFirst {15       case m: MethodSymbol if m.isPrimaryConstructor => m16     }.get.paramLists.head17 18     val companion = tpe.typeSymbol.companion19     val (toParams,fromParams) = fields.map { field =>20     val name = field.name.toTermName21     val decoded = name.decodedName.toString22     val rtype = tpe.decl.typeSignature23 24       (q"$decoded -> t.$name", q"map.asInstanceOf[$rtype]")25 26     }.unzip27 28     q"""29        new CaseClassMapConverter[$tpe] {30         def toMap: Map[String,Any] = Map(..$toParams)31         def fromMap(map: Map[String,Any]): $tpe = $companion(..$fromParams)32        }33       """34   }35 }

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

要查询在给定的对象上有什么属性和方法,可在该对象上使用(vlax-dump-object)函数。函数的语法是(vlax-dump-object object
show-methods), show-methods参数可以是nil或非nil。如果是非nil,就显示对象支持的方法,否则就不显示方法。

overloadable

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

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

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

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

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

首先,trait
CaseClassMapConverter[C]是个typeclass,代表了C类型数据的行为函数toMap和fromMap。我们同时可以看到Macro定义implicit
def
Materializer[C]是隐式的,而且是泛型的,运算结果类型是CaseClassMapConverter[C]。从这个可以推断出这个Macro定义通过Macro实现函数可以产生CaseClassMapConverter[C]实例,C可以是任何case
class类型。在函数ccToMap和mapTocc函数需要的隐式参数CaseClassMapConverter[C]实例就是由这个Macro实现函数提供的。注意我们只能用WeakTypeTag来获取类型参数C的信息。在使用quasiquotes时我们一般是在q括号中放入原始代码。在q括号内调用AST变量用$前缀(称为unquote)。对类型tpe的操作可以参考scala.reflect
api。示范调用代码在demo目录下的ConverterDemo.scala里:

  • is_power_of_two用于无符号整数

图 3-2 – Documents集合的属性和方法 

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

 

 1 import CaseClassMapConverter._ 2 object ConvertDemo extends App { 3  4   def ccToMap[C: CaseClassMapConverter]: Map[String,Any] = 5     implicitly[CaseClassMapConverter[C]].toMap 6  7   case class Person(name: String, age: Int) 8   case class Car(make: String, year: Int, manu: String) 9 10   val civic = Car("Civic",2016,"Honda")11   //println(ccToMap[Person](Person("john",18)))12   //println(ccToMap[Car]13 14   def mapTocc[C: CaseClassMapConverter](m: Map[String,Any]) =15     implicitly[CaseClassMapConverter[C]].fromMap16 17   val mapJohn = ccToMap[Person](Person("john",18))18   val mapCivic = ccToMap[Car]19   println(mapTocc[Person]20   println(mapTocc[Car])21   22 }

增加到标准库的函数

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

2.Variadic Macros: … and _ _VA_ARGS_ _

在上面这个implicit
Macros例子里引用了一些quasiquote语句。quasiquote是Scala
Macros的一个重要部分,主要替代了原来reflect
api中的reify功能,具备更强大、方便灵活的处理AST功能。Scala Def
Macros还提供了Extractor Macros,结合Scala String
Interpolation和模式匹配来提供compile time的extractor
object生成。Extractor Macros的具体用例如下:

以下函数和宏已经稳定:

提示! 以下的命令定义对显示所选图元的属性和方法会有用处。它没有提供出错处理,但是它仍是个有用的小工具。

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.)

1   import ExtractorMicros._2   val fname = "William"3   val lname = "Wang"4   val someuser =  usr"$fname,$lname"  //new FreeUser("William","Wang")5 6   someuser match {7     case usr"$first,$last" => println(s"hello $first $last")8   }
  • 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

(defun C:DUMP (/ ent obj)
  (while (setq ent (entsel “\n选择图元以获取对象数据: “))
    (setq obj (vlax-ename->vla-object (car ent)))
    (vlax-dump-object obj T)
    (vlax-release-object obj)
  )
  (princ)
)

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:

在上面这个例子里usr”???”的usr是一个pattern和extractor
object。与通常的string
interpolation不同的是usr不是一个方法,而是一个对象。这是由于模式匹配中的unapply必须在一个extractor
object内,所以usr是个object。我们知道一个object加上它的apply可以当作method来调用。也就是说如果在usr
object中实现了apply就可以用usr作为method使用,如下:

详情可查看更新说明:

提示! 属性后面的括号(RO)代表该属性为只读,在这里,所有的属性均为只读,方法后面括号内的数字表示了每一方法所需要的参数数目。

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

1   implicit class UserInterpolate(sc: StringContext) {2     object usr {3       def apply(args: String*): Any = macro UserMacros.appl4       def unapply: Any = macro UserMacros.uapl5     }6   }

要访问属性,可使用(vla-get-xxx)函数,或更通用的(vlax-get-Property)函数,两者都可以。第一个函数的语法是(vla-get-xxx
object),其中XXX是属性名。在使用(vlax-get-property)函数时,语法为(vlax-get-property object
propertyname),其中propertyname可以是双引号字符串或单引号字符串。

Suppose you later invoke the macro like this:

通过Def
Macros在编译过程中自动生成apply和unapply,它们分别对应了函数调用:

(文/开源中国)    

(vlax-get-property object property) 或 
(vla-get-property object) 或 
(vlax-get object property) 

PR(“Howdy”);

val someuser =  usr"$fname,$lname"case usr"$first,$last" => println(s"hello $first $last")

返回值是分配给该对象的指定名称属性的值,如果该对象的指定属性不存在,就会产生错误。举例来说,如果你对Line(直线)图元查询“Diameter”(直径)属性,就会产生错误。

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

下面是macro
appl的实现:

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

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

1 def appl(c: Context)(args: c.Tree*) = {2     import c.universe._3     val arglist = args.toList4     q"new FreeUser(..$arglist)"5   }

示例: 
(vlax-get-property objLine “Length”) 
(vlax-get-property objLine ‘Length) 
(vla-get-Length objLine) 

“Howdy”

主要通过q”new
FreeUser(arg1,arg2)”实现了个AST的构建。macro
uapl的实现相对复杂些,对quasiquote的应用会更深入些。首先要确定类型的primary
constructor的参数数量和名称,然后通过quasiquote的模式匹配分解出相应的sub-AST,再重新组合形成最终完整的AST:

所有这些语句都将完成同样的事情。

For the second invocation, it expands to three arguments:

 1 def uapl(c: Context)(u: c.Tree) = { 2     import c.universe._ 3     val params = u.tpe.members.collectFirst { 4       case m: MethodSymbol if m.isPrimaryConstructor => m.asMethod 5     }.get.paramLists.head.map {p => p.asTerm.name.toString} 6  7     val (qget,qdef) = params.length match { 8       case len if len == 0 => 9         (List(q""),List(q""))10       case len if len == 1 =>11         val pn = TermName(params.head)12         (List(q"def get = u.$pn"),List(q""))13       case  _ =>14         val defs = List(q"def _1 = x",q"def _2 = x",q"def _3 = x",q"def _4 = x")15         val qdefs = (params zip defs).collect {16           case  =>17             val q"def $mname = $mbody" = d18             val pn = TermName19             q"def $mname = u.$pn"20         }21         (List(q"def get = this"),qdefs)22     }23 24       q"""25         new {26           class Matcher {27             def isEmpty = false28             ..$qget29             ..$qdef30           }31           def unapply = new Matcher32         }.unapply33       """34   }35 }

属性的名称并不要求区分大小写,但本书的例子都将首字母大写,以示清晰。总的来说,你会发现以上前两项都很好用,但也有情况是要求用到后两项。特别是涉及到象Microsoft  Excel
或 Word这样的外部应用程序。第四种方式 vlax-get 是R14版遗留下来的,只为了向后兼容而已。

“weight = %d, shipping = $%.2f\n”, wt, sp

前面大部分代码就是为了形成List[Tree]
qget和qdef,最后组合一个完整的quasiquote q””” new {…}”””。

使用方法
通过图3-2的例子,你可看到Documents集合对象支持4种方法:Add、Close、Item和Open。Item方法需要使用一个参数(也就是在图3-2中显示在方法后面的那个(1)),它是由集合所引出的文档的索引和命名。

Thus, the resulting code is this:

完整的Macro实现源代码如下:

通常Item方法有一个有趣的特性,那就是它可以接受字符串或整数值参数。当给的是整数参数时,它就简单地返回集合的第(nth)条目,0为第一条目。当给的是一个字符串值时,它尝试通过它的名称属性来取得条目。Item
(名称)方法不分大小写,这是相当有用的,它不需要先对字符串进行大小写转换就可以接受名称。

printf(“Howdy”);

 1 trait User { 2   val fname: String 3   val lname: String 4 } 5  6 class FreeUser(val fname: String, val lname: String) extends User { 7   val i = 10 8   def f = 1 + 2 9 }10 class PremiumUser(val name: String, val gender: Char, val vipnum: String) //extends User11 12 object ExtractorMicros {13   implicit class UserInterpolate(sc: StringContext) {14     object usr {15       def apply(args: String*): Any = macro UserMacros.appl16       def unapply: Any = macro UserMacros.uapl17     }18   }19 }20 object UserMacros {21   def appl(c: Context)(args: c.Tree*) = {22     import c.universe._23     val arglist = args.toList24     q"new FreeUser(..$arglist)"25   }26   def uapl(c: Context)(u: c.Tree) = {27     import c.universe._28     val params = u.tpe.members.collectFirst {29       case m: MethodSymbol if m.isPrimaryConstructor => m.asMethod30     }.get.paramLists.head.map {p => p.asTerm.name.toString}31 32     val (qget,qdef) = params.length match {33       case len if len == 0 =>34         (List(q""),List(q""))35       case len if len == 1 =>36         val pn = TermName(params.head)37         (List(q"def get = u.$pn"),List(q""))38       case  _ =>39         val defs = List(q"def _1 = x",q"def _2 = x",q"def _3 = x",q"def _4 = x")40         val qdefs = (params zip defs).collect {41           case  =>42             val q"def $mname = $mbody" = d43             val pn = TermName44             q"def $mname = u.$pn"45         }46         (List(q"def get = this"),qdefs)47     }48 49       q"""50         new {51           class Matcher {52             def isEmpty = false53             ..$qget54             ..$qdef55           }56           def unapply = new Matcher57         }.unapply58       """59   }60 }

提示! 如果你熟悉Visual
Basic或VBA并经常使用默认方法和默认属性,那你要注意,这种特性在Visual
LISP是不存在的。例如,在Visual Basic中,通过以下任意一种途径来访问Item方法都是可行的:
Object.Item(12) 或 Object(12) 或
Object(“Name”) 

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

调用示范代码:

这是因为在VB或VBA中,Item方法是大多数对象的默认方法。Visual
LISP并不支持这种特性,它需要在每次使用时都阐明所有属性和方法。例如:

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

 1 object Test extends App { 2   import ExtractorMicros._ 3   val fname = "William" 4   val lname = "Wang" 5   val someuser =  usr"$fname,$lname"  //new FreeUser("William","Wang") 6  7   someuser match { 8     case usr"$first,$last" => println(s"hello $first $last") 9   }10 }

(vlax-invoke-method documents “Item”
12) 会有效果… 
(vla-item documents
“Drawing1.dwg”) 会有效果… 
(vlax-invoke-method documents
12) 不会有效果。 

 

Macros Annotation是Def
Macro重要的功能部分。对一个目标,包括类型、对象、方法等进行注释意思是在源代码编译时对它们进行拓展修改甚至完全替换。比如我们下面展示的方法注释(method
annotation):假设我们有下面两个方法:

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

// variadic.c — variadic macros

1   def testMethod[T]: Double = {2     val x = 2.0 + 2.03     Math.pow4   }5 6   def testMethodWithArgs(x: Double, y: Double) = {7     val z = x + y8     Math.pow9   }

(vla-Item documents 1) 
(vla-Item documents “Drawing1.dwg”) 
(vlax-invoke-method documents “Item” 1) 
(vlax-invoke-method documents ‘Item “Drawing1.dwg”) 

#include <stdio.h>

如果我想测试它们运行所需时间的话可以在这两个方法的内部代码前设定开始时间,然后在代码后截取完成时间,完成时间-开始时间就是运算所需要的时间了,如下:

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

#include <math.h>

1 def testMethod[T]: Double = {2     val start = System.nanoTime()3     4     val x = 2.0 + 2.05     Math.pow6     7     val end = System.nanoTime()8     println(s"elapsed time is: ${end - start}")9   }

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

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

我们希望通过注释来拓展这个方法:具体做法是保留原来的代码,同时在方法内部前后增加几行代码。我们看看这个注释的目的是如何实现的:

参数: 
Object – 一个vla-对象 
Method – 由对象显露的方法 
Arguments – 支持该方法的任何所需的参数 

int main(void)

 1 def impl(c: Context)(annottees: c.Tree*): c.Tree = { 2     import c.universe._ 3  4     annottees.head match { 5       case q"$mods def $mname[..$tpes]: $rettpe = { ..$stats }" => { 6         q""" 7             $mods def $mname[..$tpes]: $rettpe = { 8                val start = System.nanoTime() 9                val result = {..$stats}10                val end = System.nanoTime()11                println(${mname.toString} + " elapsed time in nano second = " + (end-start).toString12                result13             }14           """15       }16       case _ => c.abort(c.enclosingPosition, "Incorrect method signature!")17     }

示例: 
(vlax-invoke-method objLine “Move” point1
point2) 
(vla-Move objLine point1 point2) 
(vlax-invoke objLine “Move” point1 point2) 

{

可以看到:我们还是用quasiquote来分拆被注释的方法,然后再用quasiquote重现组合这个方法。在重组过程中增加了时间截取和列印代码。下面这行是典型的AST模式分拆(pattern
desctruction):

所有这些示例做的事情都一样。对于大部分AutoCAD对象来说都是可行的,但对于由TypeLib接口或外部应用程序或ActiveX组件输入创建的对象则不都可行。你可以将第一种方法用于外部应用程序对象,而可以将第二种方法用于内部对象。第三种方法是为了R14的向后兼容。

    double x = 48;

      case q"$mods def $mname[..$tpes]: $rettpe = { ..$stats }" => {...}

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

    double y;

用这种方式把目标分拆成重组需要的最基本部分。Macro
Annotation的实现源代码如下:

(defun MapPropertyList (object proplist)
  (foreach propset proplist
    (if (vlax-property-available-p object (car propset))
      (vlax-put-property object (car propset) (cadr propset))
    )
  )
)

    y = sqrt(x);

 1 import scala.annotation.StaticAnnotation 2 import scala.language.experimental.macros 3 import scala.reflect.macros.blackbox.Context 4  5 class Benchmark extends StaticAnnotation { 6   def macroTransform(annottees: Any*): Any = macro Benchmark.impl 7 } 8 object Benchmark { 9   def impl(c: Context)(annottees: c.Tree*): c.Tree = {10     import c.universe._11 12     annottees.head match {13       case q"$mods def $mname[..$tpes]: $rettpe = { ..$stats }" => {14         q"""15             $mods def $mname[..$tpes]: $rettpe = {16                val start = System.nanoTime()17                val result = {..$stats}18                val end = System.nanoTime()19                println(${mname.toString} + " elapsed time in nano second = " + (end-start).toString20                result21             }22           """23       }24       case _ => c.abort(c.enclosingPosition, "Incorrect method signature!")25     }26 27   }28 }

当试图在方法上使用这种方式时要特别小心,因为方法的参数列表会随着对象和方法的变化而变化。有一些方法不需要参数,而另一些则随长度变化。

    PR(1, “x = %g\n”, x);

注释的调用示范代码如下:

数据类型

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

 1 object annotMethodDemo extends App { 2  3   @Benchmark 4   def testMethod[T]: Double = { 5     //val start = System.nanoTime() 6  7     val x = 2.0 + 2.0 8     Math.pow 9 10     //val end = System.nanoTime()11     //println(s"elapsed time is: ${end - start}")12   }13   @Benchmark14   def testMethodWithArgs(x: Double, y: Double) = {15     val z = x + y16     Math.pow17   }18 19   testMethod[String]20   testMethodWithArgs(2.0,3.0)21 22 23 }

数据类型是用于描述给定对象或属性可包含的数值的类型。数据类型的例子有整数、双精度、货币、日期、字符串等等。在AutoLISP已经享受“类型无关”多年后,Visual
LISP也同样可以,但不单只这样。在AutoCAD里,你可以尽可能地通过AutoLISP来保留这种类型无关,但当涉及其它应用程序时,例如Microsoft
Excel,你将不可避免而且明智地用到数据类型。

    return 0;

有一点值得注意的是:Macro扩展是编译中遇到方法调用时发生的,而注释目标的扩展则在更早一步的方法声明时。我们下面再看注释class的例子:

发表评论

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

相关文章

网站地图xml地图