-
Notifications
You must be signed in to change notification settings - Fork 6
/
cojson-tutorial.html
815 lines (770 loc) · 43.9 KB
/
cojson-tutorial.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en-US">
<head>
<title>cojson tutorial</title>
<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
<link rel="stylesheet" type="text/css" media="screen,projection" href="http://hutorny.in.ua/wp-content/themes/veryplaintxt/style.css" title="veryplaintxt" />
<link rel="stylesheet" type="text/css" media="print" href="http://hutorny.in.ua/wp-content/themes/veryplaintxt/print.css" />
<script type="text/javascript">
var loading_url = "http://hutorny.in.ua/wp-content/plugins/thumbnail-viewer/images/loadingImage.gif";
</script>
<style type="text/css" media="all">
body{font-family:'trebuchet ms',helvetica,sans-serif;font-size:90%;}
body div#container { float: left; margin: 0 -200px 2em 0; } body div#content { margin: 3em 200px 0 0; } body div.sidebar { float: right; }
body div#content div.hentry{text-align:justify;}
body div#content h2,div#content h3,div#content h4,div#content h5,div#content h6{font-family:verdana,geneva,sans-serif;}
body div#wrapper{max-width:65em;min-width:35em;width:80%;}
body div.sidebar{text-align:left;}
</style>
<style type="text/css" id="syntaxhighlighteranchor"></style>
<style>.term { font-family: "courier new",courier,monospace; }
span.code { font-family: "courier new",courier,monospace; font-style: italic;}
table.table { border-collapse: collapse; border-spacing: 0; padding:0px; margin-left: auto; margin-right: auto; border:1px solid #000000; }
.table td{ border:1px solid #000000; vertical-align:middle; background-color:#ffffff; text-align:left; padding:4px; }
.table td:first-child { text-align:left; }
.table th{ border:1px solid #000000; background-color:#cccccc; text-align:center; font-weight:bold; padding:4px; }
.table.functions th { text-align:center; font-family: "courier new",courier,monospace; }
.table.guide { width:100%; }
.table.guide th, .table.guide td:first-child { text-align: center !important; }
.table.guide th, .table.guide td { width:20%; }
.table.guide th:last-child, .table.guide td:last-child { width:45%; }
.table.guide th:first-child, .table.guide td:first-child { width:15%; }
.table.guide td .syntaxhighlighter { margin: 0 !important; }
.table.guide td:nth-child(2) .syntaxhighlighter, .table.guide td.smaller .syntaxhighlighter { font-size: 75% !important; }
.table.guide td sup+div { width:99% !important; }
@media print {
.table.guide td:nth-child(3) { font-size: 80%; } }
</style>
</head>
<body class="wordpress single">
<div id="wrapper">
<div id="container">
<div id="content" class="hfeed">
<div id="post-617" class="hentry p1 post publish author-admin category-cojson category-projects y2015 m10 d19 h05">
<h1 class="entry-title" style="text-align:center;font-stretch: expanded;">cojson tutorial</h1>
<div class="entry-content">
<h3>1. Getting Started</h3>
<p>To start using <span class="term">cojson</span> parser/generator you need to define the structure
of your <span class="term">JSON</span> object by mapping its elements to <span class="term">cojson</span> elements,
bind them to members of your C++ class (see <a href="#section-2">Section 2</a>),
implement the interfaces for the data streams you have, and build <span class="term">cojson</span> library
with your application (see <a href="#section-3">Section 3</a>).</p>
<span id="more-617"></span>
<p>The library provides implementation for parsing/serialization of basic types. However, your application is
not limited to those types only. You can add a reader/writer for your own type and use it with
<span class="term">cojson</span> (see <a href="#section-5.1">Sections 5.1, 5.2</a>). Also, if signatures of getters/setters
in your application do not match expected in <span class="term">cojson</span>, you may define your own accessor
with the signatures you have (see <a href="#section-5.3">Section 5.3</a>).</p>
<p>It worth to mention that <span class="term">cojson</span> neither provides nor allocates any data storage.
Instead your application binds the data storage to <span class="term">cojson</span> elements.</p>
<h3 id="section-2">2. Defining Structure</h3>
<p><span class="term">JSON</span> structure is defined as a hierarchy of template functions.
Parameters of these templates define <span class="term">JSON</span> member name, data type,
and the storage access method - a member pointer or getter/setter methods.
There are four groups of template functions each having its specific purpose and name:</p>
<table class="table functions">
<tr><th style="width:6em">V<...>()</th><td>Defines a generic value</td></tr>
<tr><th>M<...>()</th><td>Defines a member</td></tr>
<tr><th>O<...>()</th><td>Defines an object mapped to C++ class</td></tr>
<tr><th>P<...>()</th><td>Defines an object property</td></tr>
</table>
<pre class="brush: cpp; collapse: true; light: false; title: Examples; toolbar: true; notranslate" title="Examples">
short val;
V<M<name,short,&val>>().read(in);
struct Pdo { int prop; };
O<Pdo, P<Pdo, short, &Pdo::prop>>().write(out);
</pre>
<h4 id="section-2.1">2.1. Values</h4>
<p>A value is defined with template <span class="term">V</span>.
Please refer to <a href="#appendix-I">Appendix I</a> for a comprehensive description of all available variants</p>
<p><a href="#RFC7159">RFC7159</a> states that a <span class="term">JSON</span> text is a serialized value.
E.g. it is not necessarily object or array.
<span class="term">cojson</span> support this feature by enabling various possibilities for data bindings.</p>
<p style="margin-bottom:0">A <span class="term">JSON</span> value can be bound to: </p>
<ul style="margin-top:0;margin-bottom:0"><li>a property of C++ class (the preferable way),</li>
<li>a pair of getter/setter method, </li>
<li>to a static variable,</li>
<li>to a function, returning pointer to a variable,</li>
<li>a pair of getter/setter functions.</li>
</ul>
<p style="margin-top:0">Values, bound to static variables or functions can also be organized in a
<span class="term">JSON</span> array or object. Such objects will further be referred as
<em>statically-bound</em> to distinguish them from <em>class-bound</em> objects</p>
<pre class="brush: cpp; collapse: true; light: false; title: Examples; toolbar: true; notranslate" title="Examples">
short v1=1;
long v2=2;
double v3=3;
long& func() { return v2; }
double get() { return v3; }
void set(double v) { v3 = v; }
V<short, &v1>().write(out); // 1,
V<long, func>().write(out); // 2,
V<double, get, set>().write(out); // 3.0
</pre>
<h4 id="section-2.2">2.2. Objects</h4>
<p>Class-bound objects are defined with variadic template <span class="code">O</span> that accepts bounding
class as the first parameter followed by the list of members. This implies that the class is defined
upfront. Statically-bound objects are defined with variadic template <span class="code">V</span> with list
of members as its parameters.</p>
<p>Members of class-bound objects are defined with templates <span class="code">P</span>,
while members of statically-bound objects are defined with templates <span class="code">M</span>.
Both of them accept member name as a parameter.</p>
<h5 id="section-2.2.1">2.2.1. Object Names</h5>
<p>Direct use of string literals as templates parameters is not supported by C++11.
Therefore <span class="term">cojson</span> defines name as a function returning pointer
to string <span class="code">const char*</span>. This workaround does not allow inline
name definitions, e.g. a name should be declared before its use.</p>
<pre class="brush: cpp; collapse: true; light: false; title: Examples; toolbar: true; notranslate" title="Examples">
short var = 1;
static constexpr const char* myname() noexcept { return "myname"; }
struct Pdo {
static short sprop;
long lprop;
static constexpr const char* propname() noexcept { return "prop"; }
} pdo;
short Pdo::sprop = 2;
V< // statically-bound object { "myname":1, "prop":2 }
M<myname,short,&var>,
M<Pdo::propname,short,&Pdo::sprop>
>().write(out);
O<Pdo, // class-bound object { "prop":2 }
P<Pdo,Pdo::propname,long,&Pdo::lprop>
>().read(pdo, in);
</pre>
<h5 id="section-2.2.2">2.2.2. Nested Objects</h5>
<p>Both class-bound and statically-bound classes allow nested objects.
Nesting in a class-bound object is achieved with a special version of template <span class="code">P</span> that
accepts additional parameter - structure definition for the nested object.</p>
<pre class="brush: cpp; collapse: true; light: false; title: Examples; toolbar: true; notranslate" title="Examples">
struct Config {
struct Wan {
long uptime;
} wan;
static constexpr const char* name_wan() noexcept { return "wan"; }
static constexpr const char* name_uptime() noexcept { return "uptime"; }
static const clas<Config>& structure() noexcept {
return
O<Config, //{
P<Config, name_wan, Config::Wan, &Config::wan, // "wan":
O<Wan, // {
P<Wan, name_uptime, decltype(Wan::uptime), &Wan::uptime> // "uptime":1
> // }
>>(); //}
}
};
</pre>
<p>Nesting in a statically-bound objects is achieved with a is generic version of template
<span class="code">M<name,value></span> where in place of value
a nested definition is used.</p>
<pre class="brush: cpp; collapse: true; light: false; title: Examples; toolbar: true; notranslate" title="Examples">
short v1=1;
static constexpr const char* p1() noexcept { return "p1"; }
static constexpr const char* p2() noexcept { return "p2"; }
const value& nested() {
return
V< // {
M<p1, // "p1" :
V< // {
M<p2,short,&v1> // "p2":1
>> // }
>(); // }
}
</pre>
<h5 id="section-2.2.3">2.2.3. Object Instances</h5>
<p>A class-bound object needs an instance of that class available for read/write operation.
There is no constraints on how that instance is allocated.
A statically bound object does not need any instance by itself, each of its members may access
different kinds of storage and some members may require data storage allocated at compile time.</p>
<h5 id="section-2.2.4">2.2.4. Extra or Missing Members</h5>
<p>If a <span class="term">JSON</span> text is missing some members defined in a
<span class="term">cojson</span> object, data fields bound to those members
will not be updated and no error condition will be set. An empty object (<span class="code">{}</span>)
is a valid input for any <span class="term">cojson</span> object.</p>
<p>Extra members, found in a <span class="term">JSON</span> text, are ignored by default.
However, this behavior may be altered with a library-wide configuration constant.</p>
<h5 id="section-2.2.5">2.2.5. Zero Objects</h5>
<p>As per <a href="#RFC7159">RFC7159</a>, a <span class="term">JSON</span> object consist
of zero or more name/value pairs (or members).
<span class="term">cojson</span> does not allow defining zero-member objects. However,
as it was mentioned above, an empty <span class="term">JSON</span> object
(<span class="code">{}</span>) is a valid input.</p>
<h4 id="section-2.3">2.3. Arrays</h4>
<p><span class="term">cojson</span> is designed to work with a predefined structure and this design
imposes certain limitation on arrays - only homogeneous array (<em>vector</em>) may have unspecified length.
A heterogeneous array (<em>ordered list</em>) must have each its item defined.
</p>
<h5 id="section-2.3.1">2.3.1. Ordered Lists</h5>
<p>Ordered lists are defined with variadic template <span class="code">V</span> with list of items as its parameters.
Every item should also be an instantiation of template <span class="code">V</span>.</p>
<pre class="brush: cpp; collapse: true; light: false; title: Examples; toolbar: true; notranslate" title="Examples">
short v1=1, v2=2, v3=3;
short& func() { return v2; }
short get() { return v3; }
void set(short v) { v3 = v; }
V< // [
V<short, &v1>, // 1,
V<short, func>, // 2,
V<short, get, set> // 3
>().write(out); // ]
</pre>
<h5 id="section-2.3.2">2.3.2. Vectors</h5>
<p>Vectors are defined with template <span class="code">V</span> and can be bound to a fixed-size C++ array
or to a <em>indexer</em> - a function returning pointer to the requested item. The first form defines a
fixed-length array while the second - a variable-length array.</p>
<pre class="brush: cpp; collapse: true; light: false; title: Examples; toolbar: true; notranslate" title="Examples">
int data[4] = { 1,2,3,4 };
unsigned count = 2;
static int* arr(unsigned i) noexcept {
return i < count ? data + i : nullptr;
}
V<int,arr>().write(out); // [1,2]
V<int,4,data>().write(out); // [1,2,3,4]
</pre>
<h5 id="section-2.3.3">2.3.3. Arrays of Objects</h5>
<p>Vectors are defined with template <span class="code">V<X,S></span>,
where X is an accessor class and S is the object's structure definition.
Two accessors - <span class="code">accessor::array</span> and <span class="code">accessor::vector</span>
implement two possible bindings to an indexer or to a static array.</p>
<pre class="brush: cpp; collapse: true; light: false; title: Examples; toolbar: true; notranslate" title="Examples">
struct Item {
static constexpr const char* name_a() noexcept { return "a"; }
static constexpr const char* name_b() noexcept { return "b"; }
short a, b;
};
Item Items[4];
void tutorial_example4(lexer&, ostream& out) {
V<accessor::array<Item,4,Items>,
O<Item,
P<Item, Item::name_a, decltype(a), &Item::a>,
P<Item, Item::name_b, decltype(b), &Item::b>
>
>().write(out);
}
</pre>
<h5 id="section-2.3.1">2.3.4. Nested Arrays</h5>
<p>An ordered list may seamlessly include another list or vector.</p>
<pre class="brush: cpp; collapse: true; light: false; title: Examples; toolbar: true; notranslate" title="Examples">
int data[4] = { 1,2,3,4 };
unsigned count = 2;
static int* arr(unsigned i) noexcept {
return i < count ? data + i : nullptr;
}
V< // [
V<int,arr>, // [1,2]
V<int,4,data> // [1,2,3,4]
>().write(out); // ]
</pre>
<p>Other variants require more complicated technique: defining a C++ type for the inner
array and implementing custom reader/writer for that type.</p>
<h5 id="section-2.3.1">2.3.5. Arrays in Classes</h5>
<p>An array field of an C++ class can be added to <span class="term">cojson</span> structure
with special form of template <span class="code">P</span>.</p>
<pre class="brush: cpp; collapse: true; light: false; title: Examples; toolbar: true; notranslate" title="Examples">
struct Pdo {
short data[4];
};
static constexpr const char* arr() noexcept { return "arr"; }
Pdo pdo;
O<Pdo,
P<Pdo, arr, short, countof(&Pdo::data), &Pdo::data>
>().read(pdo,in);
</pre>
<h5 id="section-2.3.4">2.3.6. Extra or Missing Items</h5>
<p>Array items from the <span class="term">JSON</span> text that do not fit
<span class="term">cojson</span> array definition are skipped and the
<span class="code">overrun</span> flag is set in the <span class="code">iostate::error</span>
If input array is shorter than defined in the structure, remaining items are not updated.</p>
<h4 id="section-2.4">2.4. Numbers</h4>
<p>A number is defined with template <span class="code">V</span> that accepts the destination data
and binding argument.</p>
<pre class="brush: cpp; collapse: true; light: false; title: Examples; toolbar: true; notranslate" title="Examples">
double ldv;
V<decltype(ldv),&ldv>().read(in);
</pre>
<p><span class="term">cojson</span> supports all fundamental numeric C++ types, except long double.
When a number from <span class="term">JSON</span> text does not fit the destination data type,
an overflow condition occurs. By default <span class="term">cojson</span> ignores overflow.
This can be altered with configuration constant.</p>
<h4 id="section-2.5">2.5. Strings</h4>
<p>A read/write string is defined with template <span class="code">V</span> that accepts string
length as the first parameter and data binding (directly array or a function returning pointer).
A string which is only needed for writing is defined with template <span class="code">V</span>
with a single parameter - function returning <span class="code">const char*</span> pointer.</p>
<pre class="brush: cpp; collapse: true; light: false; title: Examples; toolbar: true; notranslate" title="Examples">
char str[20];
static char* data() noexcept { return str; }
static constexpr const char* info() noexcept { return "info"; }
V<20,str>().read(in);
V<10,data>().read(in);
V<info>().write(out);
</pre>
<h4 id="section-2.6">2.6. Reading/Writing</h4>
<p>To read a <span class="term">JSON</span> text you need to pass it through
<span class="code">istream</span> interface and <span class="code">lexer</span> instance.
For writing you need an <span class="code">ostream</span> interface.
Please refer to <a href="#section-3">Section-3</a> for more details.</p>
<p>When read/write operation is complete, application, may examine stream's
<span class="code">error()</span> flags to identify possible errors.</p>
<p>If your <span class="term">JSON</span> text may start with <span class="term">BOM</span>
sequence, call <span class="code">lexer.skip_bom()</span></p>
<pre class="brush: cpp; collapse: true; light: false; title: Examples; toolbar: true; notranslate" title="Examples">
#include <iostream>
#include "cojson.hpp"
using namespace cojson;
using namespace details;
class jsonw : public ostream {
private:
std::ostream & out;
public:
inline jsonw(std::ostream& o) noexcept : out(o) {}
bool put(char_t c) noexcept {
if( out.put(c).good() ) return true;
error(details::error_t::ioerror);
return false;
}
};
class jsonr : public istream {
std::istream& in;
private:
bool get(char_t& c) noexcept {
if( in.get(c).good() ) return true;
if( in.eof() ) {
error(details::error_t::eof);
c = iostate::eos_c;
} else {
error(details::error_t::ioerror);
c = iostate::err_c;
}
return false;
}
public:
inline jsonr(std::istream& i) noexcept
: in(i) {}
};
using namespace std;
const char * hello() { return "Hello World!"; }
char answer[10];
int main(int, char**) {
jsonr inp(cin);
lexer in(inp);
jsonw out(cout);
V<hello>().write(out);
in.skip_bom();
V<sizeof(answer),answer>().read(in);
return 0;
}
</pre>
<h3 id="section-3">3. Implementing Stream Interfaces</h3>
<p><span class="term">cojson</span> provides implementation of I/O stream interfaces for
character buffer only. Luckily, these interfaces need just one method each to implement.</p>
<h4 id="section-3.1">3.1. <span class="term">istream</span> Interface</h4>
<pre class="brush: cpp; title: ; notranslate" title="">
struct my_istream : istream {
bool get(char_t& dst) noexcept {
/* Read a single character from the stream, place it in the dst and advance current position.
* Return true on success or false on error.
* In latter case put error code (iostate::eos_c or iostate::err_c) in the dst
* and set error flag with istream::error (error_t::eof or error_t::ioerror) */
}
};
</pre>
<h4 id="section-3.2">3.2. <span class="term">ostream</span> Interface</h4>
<pre class="brush: cpp; title: ; notranslate" title="">
struct my_ostream : ostream {
bool put(char_t c) noexcept {
/* Write a single character to the output
* Returns true on success or false on error
* In latter case set error flag with istream::error (error_t::ioerror) */
}
};
</pre>
<h3 id="section-4">4. Building</h3>
<p>To build application with <span class="term">cojson</span>, add its source
directory <span class="code">cojson/src</span> to the include path, add the following source files in your project:</p>
<ul>
<li>cojson/src/cojson.cpp</li>
<li>cojson/src/cojson_libdep.cpp</li>
<li>cojson/src/chartypetable.cpp</li>
</ul>
<p>With avr-g++ instead of chartypetable.cpp you may use chartypetable_progmem.cpp.
It will save you 256 bytes of RAM.</p>
<p>On some platform you may get error about missing <span class="code">type_traits</span> and
<span class="code">limits</span> files. These files are part of STD C++ library. If your
compiler does not find them, it means STD C++ lib is not available on your platform.
These files are needed for compilation only and do not introduce any run-time artifact.
You may use this file from the STD C++ lib on your build machine.</p>
<pre class="brush: plain; title: ; notranslate" title="">
$ cd cojson/include
$ ln -s /usr/include/c++/4.9/limits .
$ ln -s /usr/include/c++/4.9/type_traits .
</pre>
<p>and add cojson/include directory to the include path. Without STD C++ library
you may also get linker errors about missing symbols <span class="code">__cxa_*</span>
<span class="code">operator new, operator delete</span>.</p>
<p><span class="code">cojson</span> does not use <span class="code">new, delete</span>, if your application does the same,
you may safely stub those symbols with no implementation.</p>
<p><span class="code">__cxa_*</span> symbols are part of libc++ abi specification.
Most critical of possibly missing symbols are <span class="code">__cxa_guard_acquire</span>
and <span class="code">__cxa_guard_release</span>. You may find a simple implementation
for them in <span class="code">test/tools/avrcppfix.cpp</span> or use any other
suitable implementation.</p>
<p>On some platforms replacing <span class="code">__cxa_pure_virtual</span> with empty implementation saves sufficient
amount of program memory. Perhaps this fact is caused by presence of comprehensive diagnostic
messages in the built-in function.</p>
<h3 id="section-5">5. Advanced Topics</h3>
<p><span class="code">cojson</span> can be easily extended with new functionality that
your application may need. This is possible along two axes - (1) parsing/serializing
a value and (2) delivering the parsed value to the application.</p>
<p>Parsing and serializing a value is done via reader and writer templates.
Delivering the value - with accessor templates.</p>
<h4 id="section-5.1">5.1. User Defined <span class="term">reader</span></h4>
<p>To define a reader, provide an explicit specialization for template <span class="code">reader</span>
with the destination type and implement method <span class="code">read</span>.
This method should read input text char-by-char from the lexer, transform them into the value of
destination type and return true on success or false on error.</p>
<p>In case of an error it should not leave the lexer in the middle of a lexeme, e.g.
if error is detected on the first character it should send it back with
<span class="code">lexer.back()</span>. If a recoverable error occurs in the middle
of a lexeme, <span class="code">read</span> should consume remaining characters
of the lexeme and set error flag <span class="code">error_t::mismatch</span>.
On an irrecoverable error, such as syntax violation, reader should set
<span class="code">error_t::bad</span> flag and return <span class="code">false</span>.</p>
<pre class="brush: cpp; collapse: true; light: false; title: Examples; toolbar: true; notranslate" title="Examples">
//cojson reader for std::string
#include <string>
#include "cojson.hpp"
namespace cojson {
namespace details {
template<>
bool reader<std::string>::read(std::string& dst, lexer& in) noexcept {
std::string tmp;
bool first = true;
ctype ct;
char chr;
while( (ct=in.string(chr, first)) == ctype::string ) {
tmp += chr;
first = false;
}
if( chr ) {
in.error(error_t::bad);
return false;
}
dst = tmp;
return true;
}</pre>
<h4 id="section-5.2">5.2. User Defined <span class="term">writer</span></h4>
<p>To define a writer, provide an explicit specialization for template <span class="code">writer</span>
with the destination type and implement method <span class="code">write</span>.
This method should convert input value to string and put it char-by-char to the
output stream and return true on success or false on error.</p>
<p>Once you have reader/writer, you may used variables of that type for data bindings.</p>
<pre class="brush: cpp; collapse: true; light: false; title: Examples; toolbar: true; notranslate" title="Examples">
//cojson writer for std::string
template<>
bool writer<std::string>::write(const std::string& str, ostream& out) noexcept {
return writer<const char*>::write(str.c_str(), out);
}
//using std::string
std::string str;
V<std::string, &str>().write(out);
</pre>
<h4 id="section-5.3">5.3. User Defined <span class="term">accessor</span></h4>
<p>Accessor is a C++ template that wraps various ways of getting and setting variable
value into a uniform, compile-time interface. Namespace
<span class="term">cojson::accessor</span> contains several accessors. To create your own,
copy one most suitable from existing accessor templates, and adjust its parameters
method implementations and constant values as needed. </p>
<pre class="brush: cpp; collapse: true; light: false; title: Examples; toolbar: true; notranslate" title="Examples">
// Custom accessor via class methods.
// Differs from original by the signatures of setter/getter
template<class C, typename T, const T& (C::*G)() const,
C& (C::*S)(const T&)>
struct mymethods {
typedef C clas;
typedef T type;
static constexpr bool canget = true;
static constexpr bool canset = true;
static constexpr bool canlref= false;
static constexpr bool canrref= false;
static constexpr bool is_vector = false;
static inline constexpr bool has() noexcept { return true; }
static inline T get(const C& o) noexcept { return (o.*G)(); }
static T& lref(const C& o) noexcept; /* not possible */
static const T& rref(const C&) noexcept; /* not possible */
static inline void set(C& o, const T& v) noexcept { (o.*S)(v); }
static inline void init(T&) noexcept { }
static inline constexpr bool null(C&) noexcept {
return not config::null_is_error;
}
private:
mymethods ();
};
// Using custom accessor
struct Do {
const int& get() const;
Do& set(const int&);
static const char* name() { return "val"; }
bool read(lexer& in) {
return O<Do,P<Do,&Do::name,
mymethods<Do,int,&Do::get,&Do::set>>>().read(*this,in);
}
};</pre>
<h4 id="section-5.4">5.4. Configuring <span class="term">cojson</span></h4>
<p><span class="term">cojson</span> is configured with a set of <span class="code">constexpr</span>
values grouped in the <span class="code">config</span> structure and externalized into an includeable
file <span class="code">cojson.config</span>. You may adjust that file or create your own
<span class="code">cojson.config</span> file in another location, located on the include path
earlier than the original <span class="code">cojson.config</span>.</p>
<table class="table">
<tr><th>Symbol</th><th>Values</th><th>When specified...</th></tr>
<tr><td rowspan="3">overflow</td><td>saturate</td><td>numbers are saturated on overflow</td></tr>
<tr><td>error</td><td>overflow causes an error</td></tr>
<tr><td>ignore</td><td>overflow condition silently ignored</td></tr>
<tr><td rowspan="2">mismatch</td><td>skip</td><td>reader makes best efforts to skip such values</td></tr>
<tr><td>error</td><td>any mismatch in size or data type is treated as an irrecoverable error</td></tr>
<tr><td rowspan="2">null</td><td>skip</td><td>skip nulls by default</td></tr>
<tr><td>error</td><td>default handling for null is raising irrecoverable error</td></tr>
<tr><td rowspan="2">iostate</td><td>_notvirtual</td><td>stream's error method are not virtual</td></tr>
<tr><td>_virtual</td><td>stream's error method are virtual.
This allows to implement better error reporting by overriding iostate::error methods</td></tr>
<tr><td rowspan="2">temporary</td><td>static</td><td>temporary buffer is implemented static</td></tr>
<tr><td>_automatic</td><td>temporary buffer is implemented automatic (allocated on the stack)</td></tr>
<tr><td>temporary_size</td><td><number></td><td>overrides temporary buffer size</td></tr>
<tr><td rowspan="2">cstring</td><td>avr_progmem</td><td>use progmem for defining literals and member names (AVR only)</td></tr>
<tr><td>const_char</td><td>literals and member names are defined constant strings (const char*), default</td></tr>
</table>
<h3 id="section-R">Normative References</h3>
<ol>
<li><a href="https://tools.ietf.org/html/rfc7159" name="RFC7159" target="_blank">RFC7159</a></li>
</ol>
<h3 id="section-I">Informative References</h3>
<ol>
<li><a href="http://libcxxabi.llvm.org/spec.html" target="_blank">libc++abi Specification</a></li>
</ol>
<h3 id="appendix-I">Appendix I. Guide Map</h3>
<table class="table guide">
<tr><th><span class="term">JSON</span> text</th><th>C++</th><th><span class="term">cojson</span> definition</th><th>Comments</th></tr>
<tr><td colspan="4" style="text-align:center"><h5 style="margin:0">
Statically bound values
</h5></td></tr>
<tr><td><pre class="brush: jscript; title: ; notranslate" title="">123</pre></td><td><pre class="brush: cpp; light: true; title: ; notranslate" title="">long myvar;</pre></td><td><pre class="brush: cpp; light: true; title: ; notranslate" title="">V<long,&myvar></pre></td><td>Value bound to a variable. The simplest binding</td></tr>
<tr><td><pre class="brush: jscript; title: ; notranslate" title="">345</pre></td><td><pre class="brush: cpp; light: true; title: ; notranslate" title="">short& myfunc() {
static short myvar;
return myvar;
}</pre></td><td><pre class="brush: cpp; light: true; title: ; notranslate" title="">V<long,myfunc></pre></td><td>
Value bound to a function returning reference.
Bindings via functions allows application to detect presence
of the value in the stream and perform on-demand space allocation
</td></tr>
<tr><td><pre class="brush: jscript; title: ; notranslate" title="">678</pre></td><td><pre class="brush: cpp; light: true; title: ; notranslate" title="">int* myfunc() {
static int myvar;
return &myvar;
}</pre></td><td><pre class="brush: cpp; light: true; title: ; notranslate" title="">V<int,myfunc></pre></td><td>
Value bound to a function returning pointer. In this binding
function may return <span class="code">nullptr</span> if value
is not available. Because of this <span class="code">cojson</span>
calls it twice per value.
</td></tr>
<tr><td><pre class="brush: jscript; title: ; notranslate" title="">true</pre></td><td><pre class="brush: cpp; light: true; title: ; notranslate" title="">static bool myval;
bool my_get() {
return myval; }
void my_set(bool val) {
myval = val; }
</pre></td><td><pre class="brush: cpp; light: true; title: ; notranslate" title="">V<bool,my_get,my_set></pre></td><td>
Value bound to a getter/setter pair. This binding allows handling values
with no storage behind and perform validation of the parsed values</td></tr>
<tr><td><pre class="brush: jscript; title: ; notranslate" title="">"a string"</pre></td><td><pre class="brush: cpp; light: true; title: ; notranslate" title="">char mystring[24];</pre></td>
<td><pre class="brush: cpp; light: true; title: ; notranslate" title="">V<24,mystring></pre></td><td>
String bound to an array of char.
</td></tr>
<tr><td><pre class="brush: jscript; title: ; notranslate" title="">"output only"</pre></td><td><pre class="brush: cpp; light: true; title: ; notranslate" title="">const char* msg() {
return "output only";
}</pre></td>
<td><pre class="brush: cpp; light: true; title: ; notranslate" title="">V<msg></pre></td><td>
A string value available for writing only.
The content does not have to be constant.
</td></tr>
<tr><td><pre class="brush: jscript; title: ; notranslate" title="">"a string"</pre></td><td><pre class="brush: cpp; light: true; title: ; notranslate" title="">char* data = nullptr;
static char* mystr() {
if( data == nullptr )
data = (char*) malloc(64);
return data;}</pre></td><td><pre class="brush: cpp; light: true; title: ; notranslate" title="">V<64,mystr></pre></td><td>
String binding via a function returning pointer
</td></tr>
<tr><td><pre class="brush: jscript; title: ; notranslate" title="">[1,2,3,4]</pre></td><td><pre class="brush: cpp; light: true; title: ; notranslate" title="">int data[4];</pre></td><td><pre class="brush: cpp; light: true; title: ; notranslate" title="">V<int,4,data></pre></td><td>
Array (vector) bound to a C++ array
</td></tr>
<tr><td><pre class="brush: jscript; title: ; notranslate" title="">[1,2,3]</pre></td><td><pre class="brush: cpp; light: true; title: ; notranslate" title="">int data[4];
size_t count = 3;
int* func(size_t i) {
return i<count
? data+i : nullptr;
}</pre></td><td><pre class="brush: cpp; light: true; title: ; notranslate" title="">V<int,func></pre></td><td>
Vector binding via a function. It allows controlling array length
</td></tr>
<tr><td><pre class="brush: jscript; title: ; notranslate" title="">[2.1, "kg"]</pre></td><td><pre class="brush: cpp; light: true; title: ; notranslate" title="">double val;
char unit[8];</pre></td><td><pre class="brush: cpp; light: true; title: ; notranslate" title="">V< V<double,&val>,
V<8,unit>></pre></td><td>
Heterogeneous array bound to static variables
</td></tr>
<tr><td><pre class="brush: jscript; title: ; notranslate" title="">{"v":20}</pre></td><td><pre class="brush: cpp; light: true; title: ; notranslate" title="">int v1;</pre></td>
<td><sup style="float:left;font-size:60%"><a href="#fn1"
title="NAME here is a macro that defines a name">1</a></sup>
<pre class="brush: cpp; light: true; title: ; notranslate" title="">NAME(v)
V< M<v, int, &v1> >
</pre></td><td>
Object with a member bound to static variable.
</td></tr>
<tr><td><pre class="brush: jscript; title: ; notranslate" title="">{"u":"s"}</pre></td><td><pre class="brush: cpp; light: true; title: ; notranslate" title="">char u1[8];</pre></td>
<td><pre class="brush: cpp; light: true; title: ; notranslate" title="">NAME(u)
V< M<u, V<8, u1>> >
</pre></td><td>
Object with a member bound to a <span class="term">cojson</span> value.</td></tr>
<tr><td><pre class="brush: jscript; title: ; notranslate" title="">{"s":"x"}</pre></td><td><pre class="brush: cpp; light: true; title: ; notranslate" title="">char* data;
static char* mystr() {
return data;
}
</pre></td><td><pre class="brush: cpp; light: true; title: ; notranslate" title="">NAME(s)
V<M<s,16,mystr>></pre></td><td>
Object with a string member bound to a function
</td></tr>
<tr><td><pre class="brush: jscript; title: ; notranslate" title="">{"v":4}</pre></td><td><pre class="brush: cpp; light: true; title: ; notranslate" title="">char& myfunc() {
static char myvar;
return myvar;
}</pre></td><td><pre class="brush: cpp; light: true; title: ; notranslate" title="">NAME(v)
V<M<v,char,myfunc>></pre></td><td>
Member bound to a function returning reference.</td></tr>
<tr><td><pre class="brush: jscript; title: ; notranslate" title="">{"v":5}</pre></td><td><pre class="brush: cpp; light: true; title: ; notranslate" title="">
static double myvar;
double* myfunc() {
return &myvar;
}</pre></td><td><pre class="brush: cpp; light: true; title: ; notranslate" title="">NAME(v)
V<M<v,doublemyfunc>></pre></td><td>
Member bound to a function returning pointer.</td></tr>
<tr><td><pre class="brush: jscript; title: ; notranslate" title="">{"led":0}</pre></td><td><pre class="brush: cpp; light: true; title: ; notranslate" title="">int get() {
return PORTA&LED_BIT;}
void set(int v) {
if(v) PORTA|=LED_BIT;
else PORTA&=~LED_BIT;}</pre></td><td><pre class="brush: cpp; light: true; title: ; notranslate" title="">NAME(led)
V<M<led,
functions<int,get,set>
>></pre></td><td>
Member bound to a pair of functions
</td></tr>
<tr><td><pre class="brush: jscript; title: ; notranslate" title="">{"a":"info"}</pre></td><td><pre class="brush: cpp; light: true; title: ; notranslate" title="">const char* info() {
return "info";
}</pre></td><td><pre class="brush: cpp; light: true; title: ; notranslate" title="">NAME(a)
V<M<a,info>></pre></td><td>
Member bound to an output only string </td></tr>
<tr><td colspan="4" style="text-align:center"><h5 style="margin:0">
Class bound objects and their members
</h5></td></tr>
<tr><td><pre class="brush: jscript; title: ; notranslate" title="">{ ... }</pre></td><td><pre class="brush: cpp; light: true; title: ; notranslate" title="">struct Pdo {
//...
};</pre></td><td><pre class="brush: cpp; light: true; title: ; notranslate" title="">O<...></pre></td><td>
Class bound object</td></tr>
<tr><td><pre class="brush: jscript; title: ; notranslate" title="">{"a":"15.10.20"}</pre></td><td><pre class="brush: cpp; light: true; title: ; notranslate" title="">struct Pdo {
char a[16];
};</pre></td><td><pre class="brush: cpp; light: true; title: ; notranslate" title="">NAME(a)
O<Pdo,
P<Pdo,16,&Pod::a>></pre></td><td>
Member bound to a class property</td></tr>
<tr><td><pre class="brush: jscript; title: ; notranslate" title="">{"led":true}</pre></td><td><pre class="brush: cpp; light: true; title: ; notranslate" title="">struct Pdo {
bool get() {
return PORTA&LED_BIT;}}
void set(bool v) {
if(v) PORTA|=LED_BIT;
else PORTA&=~LED_BIT;}
};</pre></td><td class="smaller"><pre class="brush: cpp; light: true; title: ; notranslate" title="">NAME(led)
O<Pdo,
M<Pdo,led,methods<
bool,Pdo::get,Pdo::set>
>></pre></td><td>
Member bound via getter/setter methods</td></tr>
<tr><td><pre class="brush: jscript; title: ; notranslate" title="">{"a":15}</pre></td><td><pre class="brush: cpp; light: true; title: ; notranslate" title="">struct Pdo {
int a;
};</pre></td><td class="smaller"><pre class="brush: cpp; light: true; title: ; notranslate" title="">NAME(a)
O<Pdo,
P<Pdo,int,&Pod::a>></pre></td><td>
String member bound to a class property</td></tr>
<tr><td><pre class="brush: jscript; title: ; notranslate" title="">{"a":[1,2]}</pre></td><td><pre class="brush: cpp; light: true; title: ; notranslate" title="">struct Pdo {
short a[2];
};</pre></td><td class="smaller"><pre class="brush: cpp; light: true; title: ; notranslate" title="">NAME(a)
O<Pdo,
P<Pdo,a,short,
2,&Pdo::a>></pre></td><td>
Array-vector bound to an array property of class Pdo</td></tr>
<tr><td><pre class="brush: jscript; title: ; notranslate" title="">{"a":{"b":10}}</pre></td><td><pre class="brush: cpp; light: true; title: ; notranslate" title="">struct Pdo {
struct A {
int b;
} a;};</pre></td><td class="smaller"><pre class="brush: cpp; light: true; title: ; notranslate" title="">NAME(a) NAME(b)
O<Pdo, P<Pdo,a,Pdo::A,&Pdo::a,
O<Pdo::A,
P<Pdo::A,b,int,&Pdo::A::b>>>></pre></td><td>
Nested object</td></tr>
<tr><td><pre class="brush: jscript; title: ; notranslate" title="">{"a":[{"b":1},
{"b":2}]}</pre></td><td><pre class="brush: cpp; light: true; title: ; notranslate" title="">struct Pdo {
struct A {
int b;
} a[2];
};</pre></td><td class="smaller"><pre class="brush: cpp; light: true; title: ; notranslate" title="">NAME(a) NAME(b)
O<Pdo, P<Pdo,a,Pdo::A,2,Pdo::a,
O<Pdo::A,
P<Pdo::A,b,int,&Pdo::A::b>>>></pre></td><td>
Nested array of objects</td></tr>
<tr><td><pre class="brush: jscript; title: ; notranslate" title="">[{"a":1},
{"a":2}]</pre></td><td><pre class="brush: cpp; light: true; title: ; notranslate" title="">struct Pdo {
int a;
} pdo[2];</pre></td><td class="smaller"><pre class="brush: cpp; light: true; title: ; notranslate" title="">NAME(a)
V<array<Pdo,2,pdo>,
O<Pdo,P<a,int,&Pdo::a>>></pre></td><td>
Array of objects</td></tr>
</table>
<p><sup id="fn1">1</sup>NAME here is a macro that defines a <span class="term">cojson</span> name</p>
<pre class="brush: cpp; title: ; notranslate" title="">#define NAME(s) static inline constexpr const char* s() noexcept { return #s; }</pre>
</div>
</div><!-- .post -->
</div><!-- #content .hfeed -->
</div><!-- #container -->
<script type='text/javascript' src='http://hutorny.in.ua/wp-content/plugins/syntaxhighlighter/syntaxhighlighter3/scripts/shCore.js?ver=3.0.9b'></script>
<script type='text/javascript' src='http://hutorny.in.ua/wp-content/plugins/syntaxhighlighter/syntaxhighlighter3/scripts/shBrushCpp.js?ver=3.0.9b'></script>
<script type='text/javascript' src='http://hutorny.in.ua/wp-content/plugins/syntaxhighlighter/syntaxhighlighter3/scripts/shBrushJScript.js?ver=3.0.9b'></script>
<script type='text/javascript' src='http://hutorny.in.ua/wp-content/plugins/syntaxhighlighter/syntaxhighlighter3/scripts/shBrushPlain.js?ver=3.0.9b'></script>
<script type='text/javascript'>
(function(){
var corecss = document.createElement('link');
var themecss = document.createElement('link');
var corecssurl = "http://hutorny.in.ua/wp-content/plugins/syntaxhighlighter/syntaxhighlighter3/styles/shCore.css?ver=3.0.9b";
if ( corecss.setAttribute ) {
corecss.setAttribute( "rel", "stylesheet" );
corecss.setAttribute( "type", "text/css" );
corecss.setAttribute( "href", corecssurl );
} else {
corecss.rel = "stylesheet";
corecss.href = corecssurl;
}
document.getElementsByTagName("head")[0].insertBefore( corecss, document.getElementById("syntaxhighlighteranchor") );
var themecssurl = "http://hutorny.in.ua/wp-content/plugins/syntaxhighlighter/syntaxhighlighter3/styles/shThemeEclipse.css?ver=3.0.9b";
if ( themecss.setAttribute ) {
themecss.setAttribute( "rel", "stylesheet" );
themecss.setAttribute( "type", "text/css" );
themecss.setAttribute( "href", themecssurl );
} else {
themecss.rel = "stylesheet";
themecss.href = themecssurl;
}
//document.getElementById("syntaxhighlighteranchor").appendChild(themecss);
document.getElementsByTagName("head")[0].insertBefore( themecss, document.getElementById("syntaxhighlighteranchor") );
})();
SyntaxHighlighter.config.strings.expandSource = '+ expand source';
SyntaxHighlighter.config.strings.help = '?';
SyntaxHighlighter.config.strings.alert = 'SyntaxHighlighter\n\n';
SyntaxHighlighter.config.strings.noBrush = 'Can\'t find brush for: ';
SyntaxHighlighter.config.strings.brushNotHtmlScript = 'Brush wasn\'t configured for html-script option: ';
SyntaxHighlighter.defaults['light'] = true;
SyntaxHighlighter.defaults['pad-line-numbers'] = true;
SyntaxHighlighter.all();
</script>
</div><!-- #wrapper -->
</body><!-- end trasmission -->
</html>