Skip to content

Commit 22559fb

Browse files
committed
refactor(include): refactor the porting-interface
1. refactor the porting-interface and use hash-map instead of trie 2. change some detailed code 3. optimize the project please refer to the work log for details
1 parent a0c4302 commit 22559fb

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

79 files changed

+2671
-2325
lines changed

.clang-format

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ BreakConstructorInitializersBeforeComma: false
5656
BreakConstructorInitializers: BeforeComma
5757
BreakAfterJavaFieldAnnotations: true
5858
BreakStringLiterals: true
59-
ColumnLimit: 81
59+
ColumnLimit: 80
6060
CommentPragmas: '^ IWYU pragma:'
6161
CompactNamespaces: false
6262
ConstructorInitializerAllOnOneLineOrOnePerLine: false
@@ -95,7 +95,7 @@ PenaltyBreakBeforeFirstCallParameter: 300
9595
PenaltyBreakComment: 500
9696
PenaltyBreakFirstLessLess: 400
9797
PenaltyBreakString: 600
98-
PenaltyExcessCharacter: 1000
98+
PenaltyExcessCharacter: 100000
9999
PenaltyReturnTypeOnItsOwnLine: 300
100100
PointerAlignment: Right
101101
ReflowComments: true

Makefile

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ UPX = upx
55
REMOVE = del
66

77
STD = c++17
8-
LINK = -I include/stdc_implemented \
8+
LINK = -I include/interface \
9+
-I include/stdc_implemented \
910
-I src/ast \
1011
-I src/data_type \
1112
-I src/vm \
@@ -77,7 +78,7 @@ zip_release_win:
7778
$(UPX) --best --lzma bin/stamon.exe
7879

7980
zip_release_linux:
80-
$(UPX) --best --lzma bin/stamon.exe
81+
$(UPX) --best --lzma bin/stamon
8182

8283
zip_release_macos:
83-
$(UPX) --best --lzma bin/stamon.exe
84+
$(UPX) --best --lzma bin/stamon

README.md

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
* CLimber-Rong在2023年二月份开发了简陋的第一代Stamon。在2023年7月份,CLimber-Rong决定开发第二代——Stamon2(以下简称Stamon)
88
* Stamon是一门面向对象的动态编程语言。
9-
* Stamon可移植性极高,只需要实现为数不多的依赖库,Stamon就可以在你的平台上运行。
9+
* Stamon可移植性极高,只需要实现为数不多的移植接口,Stamon就可以在你的平台上运行。
1010

1111
## 下载发行版
1212

@@ -38,8 +38,6 @@
3838

3939
本项目已加入开源项目保护宣言(MOSPP),我们建议开发者们遵守此宣言所倡导的开源精神(当然,这**只具有道德约束力**)。
4040

41-
为了防止本项目遭受学术抄袭,我们决定暂时采用异步更新:发行一个版本时,先发行可执行文件,再下一次版本更新时才推送此版本的源码(即源码始终比可执行文件慢一步发布)。
42-
4341
## 开发者
4442

4543
* CLimber-Rong [Github主页](https://github.com/CLimber-Rong) [Gitee主页](https://gitee.com/QuXiangrong)

doc/CITP设计文档.md

Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
# CITP设计文档
2+
3+
### 摘要
4+
5+
在C++中,前人已经提出了多种约束接口的方式。其中,CRTP不妨是一个适用于低标准且不需要性能的静态多态设计方式。但CRTP无法约束构造函数,实现过程涉及静态转换,代码复杂。为此,我们提出了CRTP的变式——**CITP****Curiously Iterating Template Pattern,奇异递推模板模式**),并将其首次应用到Stamon当中。
6+
7+
### 需求性分析
8+
9+
我们先来引入一段典型的CRTP设计:
10+
11+
```C++
12+
/* 例1 */
13+
#include <iostream>
14+
using namespace std;
15+
16+
template<class Impl>
17+
class Constraint {
18+
public:
19+
int square(int x) {
20+
// 被开发者调用的接口
21+
return static_cast<Impl*>(this)->square(x);
22+
// 静态转换为Implementation,并调用其实现,实现接口约束
23+
}
24+
};
25+
26+
class Implementation : public Constraint<Implementation> {
27+
public:
28+
int square(int x) {
29+
// Constraint::f(int)的实现
30+
return x*x;
31+
}
32+
};
33+
34+
using Square = Implementation;
35+
36+
int main()
37+
{
38+
Square s;
39+
cout<< s.square(5) <<endl; // 运行结果:25
40+
return 0;
41+
}
42+
```
43+
44+
以上设计存在以下缺陷:
45+
46+
* 使用特殊的模板递归形式,学习成本高,难以理解
47+
* 将this静态转换为子类并调用,实现复杂
48+
* 无法安全地对构造函数进行约束
49+
50+
导致以上缺陷的原因如下:
51+
52+
* 用父类约束子类,又将子类作为类型参数传递给父类,产生奇异递归
53+
* 在父类中调用子类的成员,需要进行静态转换
54+
* 构造函数一般自底而上调用,因此父类无法简便地约束构造函数
55+
56+
在经过大量地实验性设计后,我们综合了代理模式等设计模式,提出了一种可以解决以上缺陷的方案:与CRTP将用于接口约束的类作为父类的做法相反,我们将用于接口约束的类作为子类,受约束的类作为父类,从而让接口约束从递归变为递推。
57+
58+
这种方案具有以下优点:
59+
60+
* 不需要复杂的递归形式
61+
* 让受约束的父类被子类调用,鉴于子类可以安全调用父类成员,用于约束的类无需静态转换
62+
* 用于约束的子类在构造时可以将构造函数完美转发给受约束的父类
63+
64+
综上所述,在某些情景下,CITP能比CRTP约束更多东西,且能减少思维量。
65+
66+
### 设计
67+
68+
我们来看看 _例1_ 在CITP中的设计:
69+
70+
```C++
71+
/* 例2 */
72+
#include <iostream>
73+
using namespace std;
74+
75+
class Implementation {
76+
// 实现类作为父类受约束
77+
public:
78+
int square(int x) {
79+
// 受约束接口
80+
return x*x;
81+
}
82+
};
83+
84+
template<class Impl>
85+
class Constraint : public Impl {
86+
// 用于约束的类作为子类,继承实现类
87+
public:
88+
int square(int x) {
89+
// 调用Impl的实现
90+
return Impl::square(x);
91+
}
92+
};
93+
94+
using Square = Constraint<Implementation>;
95+
// 将Implementation传递给Constraint作为约束
96+
97+
int main()
98+
{
99+
Square s;
100+
cout<< s.square(5) <<endl; // 运行结果:25
101+
return 0;
102+
}
103+
```
104+
105+
_例2_ 中,我们将Constraint作为一个模板类,并使其继承模板提供的类,因此Constraint可以完美的调用其父类(即受约束的类)的成员,包括构造函数。开发者在实现时应使用using关键字给Constraint<Implementation>赋予供用户使用的别名(例如 _例2_ 的Square)。
106+
107+
我们再看看在CITP中,如何约束构造函数:
108+
109+
```C++
110+
/* 例3 */
111+
#include <iostream>
112+
using namespace std;
113+
114+
class Implementation {
115+
// 实现类作为父类受约束
116+
int k, b;
117+
public:
118+
Implementation(int K, int B) : k(K), b(B) {};
119+
int f(int x) {
120+
// 受约束接口
121+
return k*x + b;
122+
}
123+
};
124+
125+
template<class Impl>
126+
class Constraint : public Impl {
127+
// 用于约束的类作为子类,继承实现类
128+
public:
129+
Constraint(int K, int B) : Impl(K,B) {};
130+
int f(int x) {
131+
// 调用Impl的实现
132+
return Impl::f(x);
133+
}
134+
};
135+
136+
using Linear = Constraint<Implementation>;
137+
// 将Implementation传递给Constraint作为约束
138+
139+
int main()
140+
{
141+
Linear l(4,5);
142+
cout<< l.f(5) <<endl; // 运行结果:25
143+
return 0;
144+
}
145+
```
146+
147+
可以看到,Constraint只需要正常编写构造函数,并转发给父类的构造函数,即可实现构造函数的约束。此外,由于析构函数的调用顺序是从子类到父类,因此Constraint哪怕没有编写析构函数,Implementation的析构函数也照样会被正常调用。但这也使得析构函数无法收到约束——CITP难以保证受约束的类一定拥有开发者手写的析构函数。
148+
149+
CITP的设计中,约束重载运算符的方式和约束普通方法的方式是不同的。我们将 _例3_ 中的函数``int f(int)``改装成``int operator<<(int)``,来看看CITP是如何约束重载运算符的:
150+
151+
```C++
152+
/* 例4 */
153+
#include <iostream>
154+
using namespace std;
155+
156+
class Implementation {
157+
// 实现类作为父类受约束
158+
int k, b;
159+
public:
160+
Implementation(int K, int B) : k(K), b(B) {};
161+
int operator<<(int x) {
162+
// 受约束接口
163+
return k*x + b;
164+
}
165+
};
166+
167+
template<class Impl>
168+
class Constraint : public Impl {
169+
// 用于约束的类作为子类,继承实现类
170+
public:
171+
Constraint(int K, int B) : Impl(K,B) {};
172+
int operator<<(int x) {
173+
// 调用Impl的实现
174+
return Impl::operator<<(x);
175+
}
176+
};
177+
178+
using Linear = Constraint<Implementation>;
179+
// 将Implementation传递给Constraint作为约束
180+
181+
int main()
182+
{
183+
Linear l(4,5);
184+
cout<< (l<<5) <<endl; // 运行结果:25
185+
return 0;
186+
}
187+
```
188+
189+
### 结论
190+
191+
CITP相较于CRTP,更专注于接口约束,是一个适用于低C++标准的高性能方案。但CITP目前仍然存在缺陷:
192+
193+
* 无法具体约束函数的行为,也无法约束析构函数
194+
* 在传递参数、返回值时也可能发生参数、返回值的隐式转换且无法约束这种转换
195+
* 如果CITP模板代码处于未使用状态,编译器可能不会进行接口约束检查
196+
197+
这些问题正在研究当中,有待解决。

doc/SFN机制文档.md

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,20 +43,23 @@ void sfn_print(SFN_PARA_LIST);
4343
4444
##### 实现外部函数
4545
46-
外部函数可以使用依赖库上的函数,对于移植在平台上的开发者而言,也可以引入更多平台特有的函数进行操作。外部函数应该使用``stamon::exception::sfn::SFNError``作为异常生成函数,开发者也可以引入更多细化的异常生成函数。
46+
外部函数可以使用移植接口上的函数,对于移植在平台上的开发者而言,也可以引入更多平台特有的函数进行操作。外部函数应该使用``stamon::exception::sfn::SFNError``作为异常生成函数,开发者也可以引入更多细化的异常生成函数。
4747
4848
``sfn_print``的实际实现如下:
4949
5050
```C++
5151
void sfn_print(SFN_PARA_LIST) {
52+
53+
using namespace stamon;
54+
5255
STMException *ex = sfn.ex;
53-
stamon::datatype::DataType *val = arg->data;
54-
if (val->getType() != stamon::datatype::StringTypeID) {
55-
THROW(stamon::exception::sfn::SFNError(
56+
datatype::DataType *val = arg->data;
57+
if (val->getType() != datatype::StringTypeID) {
58+
THROW(exception::sfn::SFNError(
5659
"sfn_print()", "invalid type"));
5760
return;
5861
}
59-
platform_print(((stamon::datatype::StringType *) val)->getVal());
62+
platform_print(((datatype::StringType *) val)->getVal());
6063
return;
6164
}
6265
```
@@ -81,4 +84,4 @@ SFN(STMException *e, vm::ObjectManager *objman) {
8184
}
8285
```
8386
84-
``sfn_functions``是一个``StringMap``,存储的值类型是符合外部函数原型的函数指针。在构造函数中加入利用``sfn_functions.put``来指定端口号及其对应的外部函数
87+
``sfn_functions``是一个Map,存储的值类型是符合外部函数原型的函数指针。在构造函数中加入利用``sfn_functions.put``来指定端口号及其对应的外部函数

doc/代码速览指南.md

Lines changed: 16 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
# 代码速览指南(第三版)
1+
# 代码速览指南(第四版)
2+
3+
### 第四版前言
4+
5+
进一步优化了表达方式,将源码目录功能和阅读顺序统一为一个表格。
26

37
### 第三版前言
48

@@ -23,7 +27,7 @@
2327

2428
* 文件后缀
2529

26-
本项目中,依赖库的源码后缀统一为``.h````.hpp``格式;主体实现部分中,每一个源码子目录由一个``.hpp``文件来统领其他的``.cpp``文件(要求开发者只需包含此``.hpp``文件即可调用该子目录下的所有代码)。
30+
本项目中,移植接口的源码后缀统一为``.h````.hpp``格式;主体实现部分中,每一个源码子目录由一个``.hpp``文件来统领其他的``.cpp``文件(要求开发者只需包含此``.hpp``文件即可调用该子目录下的所有代码)。
2731

2832
``.hpp````.cpp``都可以用于代码的定义和实现,两个后缀名在本项目当中仅用来区分哪个文件统领了源码目录。
2933

@@ -33,43 +37,26 @@
3337

3438
由于本项目并未采用“定义与实现分离开来,后期统一链接”的形式。
3539

36-
我们不建议开发者将此项目中的``#include``看作C/C++中的包含,而是看作import(类似java,py的设计)。
40+
我们不建议开发者将此项目中的``#include``看作C/C++中的包含,而是看作import(类似java,python的设计)。
3741

3842
因为本项目的编译指令只有一条,不存在链接,因此我们不建议两个文件之间相互包含(请尽量编写不循环引用的代码)。
3943

40-
### 项目架构
44+
### 项目架构及源码阅读顺序
4145

4246
我们来介绍与源码有关的目录:
47+
我们将用一个表格来解释每个目录的用途。目录从顶至底,也是推荐的源码目录阅读顺序。
4348

4449
|目录名|用途|
4550
|:-|:-|
46-
|``include``|该目录用于存放依赖库文件,此目录又有多个子目录。其中``template``子目录用于存放依赖库模板,其余子目录是依赖库的不同平台实现。|
47-
|``src``|该目录存储主体实现部分的代码,与依赖库实现分割开来,以此实现更强的可移植性。该目录下存放有若干子目录和项目主要文件(例如程序的入口源码)。|
51+
|``include``|该目录用于存放移植接口的定义和不同方法的实现,此目录又有多个子目录。其中``interface``子目录用于存放移植接口的定义,其余子目录是移植接口的不同方法实现。移植接口的更多介绍位于``移植接口开发指导.md``|
4852
|``src/ast``|该目录存储抽象语法树(AST)的数据结构,主要用于语法分析。|
49-
|``src/data_type``|该目录存储stamon数据类型的数据结构。|
50-
|``src/bin-include``|该目录存储Stamon的标准库源码。|
51-
|``src/exception``|该目录存储报错信息的生成函数。|
5253
|``src/config``|该目录存储程序运行的默认配置参数。|
54+
|``src/exception``|该目录存储报错信息的生成函数。|
5355
|``src/compiler``|该目录存储编译器的实现。|
54-
|``src/ir``|该目录存储Stamon的中间代码数据结构,中间代码生成器以及解析器。|
55-
|``src/sfn``|该目录存储Stamon调用虚拟机外部库的实现。|
56+
|``src/data_type``|该目录存储stamon数据类型的数据结构。|
5657
|``src/vm``|该目录存储虚拟机的实现。|
58+
|``src/sfn``|该目录存储Stamon调用虚拟机外部库的实现。|
59+
|``src/ir``|该目录存储Stamon的中间代码数据结构,中间代码生成器以及解析器。|
5760
|``src/action``|该目录用于存储分步行动机制的实现代码(包括一些文件的编解码实现)。|
58-
59-
### 源码阅读顺序
60-
61-
我们建议开发者先从基础出发,一步步看到主函数的实现。
62-
63-
因此一个合理的源码阅读流程是(以目录为单位):
64-
65-
* ``include``
66-
* ``src/ast``
67-
* ``src/config``
68-
* ``src/exception``
69-
* ``src/compiler``
70-
* ``src/vm``
71-
* ``src/sfn``
72-
* ``src/action``
73-
* ``src/ir``
74-
* ``src``
75-
* ``src/bin-include``
61+
|``src``|该目录存储主体实现部分的代码,与移植接口实现分割开来,以此实现更强的可移植性。该目录下存放有若干子目录和项目主要文件(例如程序的入口源码)。|
62+
|``src/bin-include``|该目录存储Stamon的标准库源码。|

doc/依赖库开发指导.md

Lines changed: 0 additions & 7 deletions
This file was deleted.

0 commit comments

Comments
 (0)