Upload packaging folder
[platform/upstream/iotjs.git] / docs / devs / Inside-IoT.js-Validated-Struct.md
1 Validated Struct
2 ================
3
4 Validated struct is C struct wrapper for encapsulation and validity check.
5
6 * Validated Struct Declaration
7 * Constructors, Destructor, Methods
8 * Ownership of validated struct instance
9   * Case 1: Validated struct instance as local variable
10   * Case 2: Validated struct instance as parameter & return
11   * Case 3: Validated struct instance as member variable of other struct
12   * Case 4: Validated struct instance as data of asynchronous execution
13
14 # Validated Struct Declaration
15
16 ```c
17 typedef struct {
18   int a;
19   void* b;
20 } IOTJS_VALIDATED_STRUCT(iotjs_myclass_t);
21 ```
22
23 Above struct will make the member variable encapsulated by wrapping real members with wrapper like below.
24
25 ```c
26 typedef struct {
27   int a;
28   void* b;
29 } iotjs_myclass_t_impl_t;
30
31 typedef struct {
32   iotjs_myclass_impl_t unsafe;
33   /* More members for struct validity check exist in debug mode */
34 } iotjs_myclass_t;
35
36 int main() {
37   iotjs_myclass_t x;
38 }
39 ```
40
41 Only wizards will access the members directly by using `x.unsafe.a`, `x.unafe.b`, ... . Otherwize the members are only accessible with its accessor function.
42
43 See `src/iotjs_def.h` for more details on real implementation.
44
45 # Constructors, Destructor, Methods
46
47 You should create C++-like constructors, destructor and methods with provided accessor. Then you can access the encapsulated member variables using `_this` variable, which has almost same role with C++ `this` keyword.
48 You must call `destroy` for every validated structs you've created.
49
50 ```c
51 /* Constructor */
52 iotjs_myclass_t iotjs_myclass_create(int a) {
53   iotjs_myclass_t instance;
54   IOTJS_VALIDATED_STRUCT_CONSTRUCTOR(iotjs_myclass_t, &instance);
55
56   _this->a = a;
57   _this->b = malloc(a);
58
59   return instance;
60 }
61
62 /* Destructor */
63 void iotjs_myclass_destroy(iotjs_myclass_t* instance) {
64   IOTJS_VALIDATED_STRUCT_DESTRUCTOR(iotjs_myclass_t, instance);
65   free(_this->b);
66 }
67
68 /* Method */
69 int iotjs_myclass_get_a(iotjs_myclass_t* instance) {
70   IOTJS_VALIDATED_STRUCT_METHOD(iotjs_myclass_t, instance);
71   return _this->a;
72 }
73
74 int main() {
75   /* Validated struct as local variable */
76   iotjs_myclass_t local_instance = iotjs_myclass_create(3);
77   printf("%d\n", iotjs_myclass_get_a(&local_instance));
78   iotjs_myclass_destroy(&local_instance);
79   return 0;
80 }
81 ```
82
83 # Ownership of validated struct instance
84
85 The ground rule is:
86
87 * Use `iotjs_classname_t` typed variable if the variable *is* responsible for destruction of instance.
88 * Use `iotjs_classname_t*` typed variable if the variable *is not* responsible for destruction of instance.
89
90 Below Case 1 ~ Case 4 shows the case-by-case example of the ownership rule.
91
92 ## Case 1: Validated struct instance as local variable
93 The `local_instance` variable in previous example was the local instance of validated struct.
94 Since `local_instance` should be destructed inside the function scope, `iotjs_myclass_t` type was used.
95
96 ## Case 2: Validated struct instance as parameter & return
97 Previous example also included the example of validated struct instance as parameter and return.
98 When accessing member variable `a` by calling `iotjs_myclass_get_a()`,
99 `iotjs_myclass_t*` type was used as the parameter type, since it *does not* move the responsibility to destruct the instance.
100
101 And when returning the newly created instance by calling `iotjs_myclass_create()`,
102 `iotjs_myclass_t` type was used as return type, since it *does* move the responsibility to destruct the instance.
103
104 ## Case 3: Validated struct instance as member variable of other struct
105
106 ```c
107 /* Validated struct as member variable of other struct */
108
109 typedef struct {
110   iotjs_myclass_t member_instance;
111 } IOTJS_VALIDATED_STRUCT(iotjs_otherclass_t)
112
113 iotjs_otherclass_t iotjs_otherclass_create() {
114   /* Initialization steps for iotjs_otherclass_t */
115   _this->member_instance = iotjs_myclass_create(3);
116 }
117
118 void iotjs_otherclass_destroy() {
119   /* Finalization steps for iotjs_otherclass_t */
120   iotjs_myclass_destroy(&_this->member_instance);
121 }
122 ```
123
124 In the case above, `iotjs_myclass_t` instance is used as member variable of other class.
125 Since `iotjs_otherclass_t` is responsible for finalizing the `member_instance`,
126 it owns the variable as `iotjs_myclass_t` type, not pointer type.
127
128 ## Case 4: Validated struct instance as data of asynchronous execution
129 Another usecase would be using validated struct as callback data.
130 Currently, our all asynchronous datas are wrapped with `iotjs_*wrap_t` type,
131 and they are destructed automatically.
132
133 ```c
134 /*
135  * Public APIs in iotjs_module_fs.h
136  */
137
138 typedef struct {
139   iotjs_reqwrap_t reqwrap;
140   uv_fs_t req;
141 } IOTJS_VALIDATED_STRUCT(iotjs_fsreqwrap_t);
142
143 iotjs_fsreqwrap_t* iotjs_fsreqwrap_create(const iotjs_jval_t* jcallback);
144 void iotjs_fsreqwrap_dispatched(iotjs_fsreqwrap_t* fsreqwrap);
145 ```
146
147 As you can see, constructor returns the `iotjs_fsreqwrap_t*` type,
148 because it does not pass the responsibility to destruct the return value.
149 It is destructed when request is dispatched, which can be informed by calling `iotjs_fsreqwrap_dispatched()`.
150 The destructor `iotjs_fsreqwrap_destroy()` is hidden in c file.
151
152 ```c
153 /*
154  * Implementation in iotjs_module_fs.c
155  */
156
157 iotjs_fsreqwrap_t* iotjs_fsreqwrap_create(const iotjs_jval_t* jcallback) {
158   iotjs_fsreqwrap_t* fsreqwrap = IOTJS_ALLOC(iotjs_fsreqwrap_t);
159   IOTJS_VALIDATED_STRUCT_CONSTRUCTOR(iotjs_fsreqwrap_t, fsreqwrap);
160   iotjs_reqwrap_initialize(&_this->reqwrap, jcallback, (uv_req_t*)&_this->req);
161   return fsreqwrap;
162 }
163
164 static void iotjs_fsreqwrap_destroy(iotjs_fsreqwrap_t* fsreqwrap) { // private function
165   IOTJS_VALIDATED_STRUCT_DESTRUCTOR(iotjs_fsreqwrap_t, fsreqwrap);
166   uv_fs_req_cleanup(&_this->req);
167   iotjs_reqwrap_destroy(&_this->reqwrap);
168   IOTJS_RELEASE(fsreqwrap);
169 }
170
171 void iotjs_fsreqwrap_dispatched(iotjs_fsreqwrap_t* fsreqwrap) {
172   IOTJS_VALIDATED_STRUCT_METHOD(iotjs_fsreqwrap_t, fsreqwrap);
173   iotjs_fsreqwrap_destroy(fsreqwrap);
174 }
175
176 /*
177  * Use of iotjs_fsreqwrap_t
178  */
179
180 void callback(uv_fs_t* req) {
181     do_something(req);
182     iotjs_fsreqwrap_dispatched(req); /* Call iotjs_*reqwrap_dispatched() when callback called */
183 }
184
185 void request(iotjs_jval_t* jcallback) {
186     iotjs_fsreqwrap_t* wrap = iotjs_fsreqwrap_create(jcallback);
187     uv_fs_request(loop, wrap->req, callback);
188 }
189 ```
190
191 In the case of tuv request wrapper, `iotjs_*reqwrap_dispatched()` should be called when the request has been dispatched.
192 In the case of tuv handle wrapper, `iotjs_handlewrap_close()` should be called when the handle has been closed.
193 in the case of JavaScript object wrapper, you don't have to do anything because JavaScript engine will call the destructor when the object becomes inaccessible.
194
195