-
Notifications
You must be signed in to change notification settings - Fork 21
/
Copy pathmsetl_example.cpp
2322 lines (2013 loc) · 93.5 KB
/
msetl_example.cpp
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
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// Copyright (c) 2015 Noah Lopez
// Use, modification, and distribution is subject to the Boost Software
// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
/*
This example file has become quite large (and has spilled into msetl_example2.cpp) and holds examples for many data
types. Your best bet is probably to use a find/search to get to the data type your interested in.
*/
#include "msetl_example_defs.h"
#include "msetl_example2.h"
#include "msetl_example3.h"
#ifndef EXCLUDE_MSETL_EXAMPLE
#include "mseprimitives.h"
#include "mseregistered.h"
#include "msecregistered.h"
#include "msenorad.h"
#include "mserefcounting.h"
#include "msescope.h"
#include "mseasyncshared.h"
#include "msepoly.h"
#include "msemsearray.h"
#include "msemstdarray.h"
#include "msemsevector.h"
#include "msemstdvector.h"
#include "mseivector.h"
#include "msevector_test.h"
#include "msemstdstring.h"
#include "mseregisteredproxy.h"
#include "msenoradproxy.h"
/* This block of includes is required for the mse::TRegisteredRefWrapper example */
#include <algorithm>
#include <list>
#include <vector>
#include <iostream>
#include <numeric>
#include <random>
#include <functional>
#ifdef _MSC_VER
#pragma warning( push )
#pragma warning( disable : 4100 4456 4189 4702 )
#endif /*_MSC_VER*/
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wmissing-braces"
#pragma clang diagnostic ignored "-Wtautological-compare"
#pragma clang diagnostic ignored "-Wunused-variable"
#pragma clang diagnostic ignored "-Wunused-function"
#pragma clang diagnostic ignored "-Wunused-but-set-variable"
#else /*__clang__*/
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-variable"
#pragma GCC diagnostic ignored "-Wunused-function"
#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
#endif /*__GNUC__*/
#endif /*__clang__*/
class H {
public:
/* Just an example of a templated member function. In this case it's a static one, but it doesn't have to be.
You might consider templating pointer parameter types to give the caller some flexibility as to which kind of
(smart/safe) pointer they want to use. */
template<typename _Tpointer>
static int foo4(_Tpointer A_ptr) { return A_ptr->b; }
template<typename _Tpointer, typename _Tvector>
static int foo5(_Tpointer A_ptr, _Tvector& vector_ref) {
int tmp = A_ptr->b;
int retval = 0;
vector_ref.clear();
if (A_ptr) {
retval = A_ptr->b;
}
else {
retval = -1;
}
return retval;
}
template<class _TString1Pointer, class _TString2Pointer>
static std::string foo6(_TString1Pointer i1ptr, _TString2Pointer i2ptr) {
return (*i1ptr) + (*i2ptr);
}
/* This function will be used to demonstrate using rsv::as_a_returnable_fparam() to enable template functions to
return one of their function parameters, potentially of the scope reference variety which would otherwise be
rejected (with a compile error) as an unsafe return value. */
template<class _TString1Pointer, class _TString2Pointer>
static auto longest(const _TString1Pointer string1_ptr, const _TString2Pointer string2_ptr) {
auto l_string1_ptr = mse::rsv::as_a_returnable_fparam(string1_ptr);
auto l_string2_ptr = mse::rsv::as_a_returnable_fparam(string2_ptr);
if (l_string1_ptr->length() > l_string2_ptr->length()) {
/* If string1_ptr were a regular TXScopeFixedPointer<mse::nii_string> and we tried to return it
directly instead of l_string1_ptr, it would have induced a compile error. */
return mse::return_value(l_string1_ptr);
}
else {
/* mse::return_value() usually just returns its input argument unmolested, but in this case, where the
argument was obtained from the mse::rsv::as_a_returnable_fparam() it will convert it back the type of
the original function parameter (thereby removing the "returnability" attribute that was added by
mse::rsv::as_a_returnable_fparam()). */
return mse::return_value(l_string2_ptr);
}
}
/* This function will be used to demonstrate nested function calls (safely) returning scope pointer/references. */
template<class _TString1Pointer, class _TString2Pointer>
static auto nested_longest(const _TString1Pointer string1_ptr, const _TString2Pointer string2_ptr) {
auto l_string1_ptr = mse::rsv::as_a_returnable_fparam(string1_ptr);
auto l_string2_ptr = mse::rsv::as_a_returnable_fparam(string2_ptr);
/* Note that with functions (potentially) returning a scope reference parameter (or an object derived
from a scope reference parameter), you generally want the function to be a template function with the
scope reference parameters types being (deduced) template parameters, as with this function, rather
than more explicitly specified scope reference types or template types. The reason for this is that in
the case of nested function calls, the number of nested function calls from which a scope reference
object can be (safely) returned is embedded in the scope reference object's type. That is, the exact
type of a returnable scope reference object depends on how many (levels of) nested function calls it
has been passed through. And you generally want your functions that return scope reference objects to
preserve the exact type of the scope reference passed, otherwise you may not be allowed (i.e. induced
compile error) to return the scope reference all the way back to the scope it originated from. */
return mse::return_value(longest(l_string1_ptr, l_string2_ptr));
}
struct CE {
mse::nii_string m_string1 = "abcde";
};
/* This function demonstrates scope reference objects inheriting the "returnability" trait from the reference objects
from which they were derived. */
template<class _TPointer1>
static auto xscope_string_const_section_to_member_of_CE(const _TPointer1 CE_ptr) {
auto returnable_CE_ptr = mse::rsv::as_a_returnable_fparam(CE_ptr);
/* "Pointers to members" based on returnable pointers inherit the "returnability". */
auto returnable_cpointer_to_member = mse::make_xscope_const_pointer_to_member_v2(returnable_CE_ptr, &CE::m_string1);
/* "scope nrp string const sections" based on returnable pointers (or iterators) inherit the "returnability". */
auto returnable_string_const_section = mse::make_xscope_string_const_section(returnable_cpointer_to_member);
/* Subsections of returnable sections inherit the "returnability". */
auto returnable_string_const_section2 = mse::make_xscope_subsection(returnable_string_const_section, 1, 3);
return mse::return_value(returnable_string_const_section2);
}
template<class _TPointer1>
static auto nested_xscope_string_const_section_to_member_of_CE(const _TPointer1& CE_ptr) {
auto returnable_CE_ptr = mse::rsv::as_a_returnable_fparam(CE_ptr);
return mse::return_value(xscope_string_const_section_to_member_of_CE(returnable_CE_ptr));
}
/* This function will be used to demonstrate using rsv::as_an_fparam() to enable template functions to accept scope
pointers to temporary objects. */
template<class _TPointer1, class _TPointer2>
static bool second_is_longer(_TPointer1&& string1_xscpptr, _TPointer2&& string2_xscpptr) {
auto l_string1_xscpptr = mse::rsv::as_an_fparam(std::forward<decltype(string1_xscpptr)>(string1_xscpptr));
auto l_string2_xscpptr = mse::rsv::as_an_fparam(std::forward<decltype(string2_xscpptr)>(string2_xscpptr));
return (l_string1_xscpptr->length() > l_string2_xscpptr->length()) ? false : true;
}
mse::nii_string m_string1 = "initial text";
};
/* User-defined classes need to be declared as (safely) shareable in order to be accepted by the access requesters. */
typedef mse::rsv::TAsyncShareableAndPassableObj<H> ShareableH;
int main(int argc, char* argv[]) {
mse::msevector_test msevector_test;
msevector_test.run_all();
mse::msevector_test2 msevector_test2;
msevector_test2.test1();
{
/**********************/
/* mstd::vector<> */
/**********************/
/* mse::mstd::vector<> is an almost "completely safe" (bounds checked, iterator checked and memory managed)
implementation of std::vector. Here we'll demonstate the safety of the insert() member function. */
double a1[3] = { 1.0, 2.0, 3.0 };
double *d_pointer1 = &(a1[0]);
double a2[3] = { 4.0, 5.0, 360 };
double *d_pointer2 = &(a2[0]);
mse::mstd::vector<double> v1;
//v1.insert(v1.begin(), d_pointer1, d_pointer2); /* not good */
/* std::vector supports "naked" pointers as parameters to the insert() member
function so mse::mstd::vector does also. Unfortunately there is no way to ensure
that the naked pointer parameters have valid values. */
#ifdef MSVC2010_COMPATIBLE
mse::mstd::vector<double> v2(a1, a1+3);
mse::mstd::vector<double> v3(a2, a2+3);
mse::mstd::vector<double> v4;
#else /*MSVC2010_COMPATIBLE*/
mse::mstd::vector<double> v2 = { 1.0, 2.0, 3.0 };
mse::mstd::vector<double> v3 = { 4.0, 5.0, 360 };
mse::mstd::vector<double> v4;
#endif /*MSVC2010_COMPATIBLE*/
#ifndef MSE_MSTDVECTOR_DISABLED
MSE_TRY {
v4.insert(v4.begin(), v2.begin(), v3.begin());
}
MSE_CATCH_ANY {
std::cerr << "expected exception" << std::endl;
/* The exception is triggered by a comparision of incompatible "safe" iterators. */
}
#endif // !MSE_MSTDVECTOR_DISABLED
/* And of course the iterators can be used with the standard algorithms, just like those of std::vector. */
std::sort(v3.begin(), v3.end());
{
#ifdef MSE_HAS_CXX17
#ifndef MSE_MSTDVECTOR_DISABLED
/* deduction guide example */
auto str1 = std::string("abcd");
auto vector2 = mse::mstd::vector(str1.cbegin(), str1.cend());
assert('b' == vector2[1]);
#endif // !MSE_MSTDVECTOR_DISABLED
#endif /* MSE_HAS_CXX17 */
}
}
{
/* Here's how mse::mstd::vector<>::iterator handles occurrences of "use-after-free". */
typedef mse::mstd::vector<int> vint_type;
mse::mstd::vector<vint_type> vvi;
{
vint_type vi;
vi.push_back(5);
vvi.push_back(vi);
}
auto vi_it = vvi[0].begin();
vvi.clear();
#if !defined(MSE_MSTDVECTOR_DISABLED) && !defined(MSE_MSTD_VECTOR_CHECK_USE_AFTER_FREE)
MSE_TRY {
/* At this point, the vint_type object is cleared from vvi, but (with the current library implementation) it has
not actually been deallocated/destructed yet because it "knows" that there is an iterator, namely vi_it, that is
still referencing it. It will be deallocated when there are no more iterators referencing it. */
auto value = (*vi_it); /* In debug mode this will fail an assert. In non-debug mode it'll just work (safely). */
assert(5 == value);
vint_type vi2;
vi_it = vi2.begin();
/* The vint_type object that vi_it was originally pointing to is now deallocated/destructed, because vi_it no longer
references it. */
}
MSE_CATCH_ANY {
/* At present, no exception will be thrown. With future library implementations, maybe. */
std::cerr << "potentially expected exception" << std::endl;
}
#endif // !defined(MSE_MSTDVECTOR_DISABLED) && !defined(MSE_MSTD_VECTOR_CHECK_USE_AFTER_FREE)
}
{
/* If the vector is declared as a "scope" object (which basically indicates that it is declared
on the stack), then you can use "scope" iterators. While there are limitations on when they can
be used, scope iterators would be the preferred iterator type where performance is a priority
as they don't require extra run-time overhead to ensure that the vector has not been prematurely
deallocated. */
/* Here we're declaring an vector as a scope object. */
mse::TXScopeObj<mse::mstd::vector<int>> vector1_scpobj = mse::mstd::vector<int>{ 1, 2, 3 };
{
/* Here we're obtaining a scope iterator to the vector. */
auto scp_iter1 = mse::mstd::make_xscope_begin_iterator(&vector1_scpobj);
auto scp_iter2 = mse::mstd::make_xscope_end_iterator(&vector1_scpobj);
std::sort(scp_iter1, scp_iter2);
auto scp_citer3 = mse::mstd::make_xscope_begin_const_iterator(&vector1_scpobj);
scp_citer3 = scp_iter1;
scp_citer3 = mse::mstd::make_xscope_begin_const_iterator(&vector1_scpobj);
scp_citer3 += 2;
auto res1 = *scp_citer3;
auto res2 = scp_citer3[0];
/* Here we demonstrate the case where the vector is a member of a class/struct declared as a scope object. */
class CContainer1 {
public:
CContainer1() : m_vector({ 1, 2, 3 }) {}
mse::mstd::vector<int> m_vector;
};
mse::TXScopeObj<CContainer1> container1_scpobj;
auto container1_m_vector_scpptr = mse::make_xscope_pointer_to_member_v2(&container1_scpobj, &CContainer1::m_vector);
auto scp_citer4 = mse::mstd::make_xscope_begin_iterator(container1_m_vector_scpptr);
scp_citer4++;
auto res3 = *scp_citer4;
/* Note that scope iterators, while they exist, have the effect of "structure locking" their associated container.
That is, while a scope iterator exists, the "structure" (i.e. size or capacity) of the target container will remain
unchanged. Attempting any operation that would affect the structure would result in an exception. This property
allows us to (safely) obtain a (direct) scope pointer to the scope iterator's target element. */
auto scp_ptr1 = mse::xscope_pointer(scp_iter1);
auto res4 = *scp_ptr1;
}
/* After all the scope pointers have gone out of scope, you may again perform operations that affect the container's
"structure" (i.e. size or capacity). */
vector1_scpobj.push_back(4);
}
{
/*****************/
/* ivector<> */
/*****************/
/* mse::ivector<> is a safe vector like mse::mstd::vector<>, but its iterators behave more like list iterators
than standard vector iterators. That is, upon insert or delete, the iterators continue to point to the same
item, not (necessarily) the same position. And they don't become "invalid" upon insert or delete, unless the
item they point to is deleted. */
#ifdef MSVC2010_COMPATIBLE
int a1[4] = { 1, 2, 3, 4 };
mse::ivector<int> v(a1, a1 + 4);
#else /*MSVC2010_COMPATIBLE*/
mse::ivector<int> v = { 1, 2, 3, 4 };
#endif /*MSVC2010_COMPATIBLE*/
mse::ivector<int>::ipointer ip1 = v.begin();
ip1 += 2;
assert(3 == (*ip1));
auto ip2 = v.begin();
v.erase(ip2); /* remove the first item */
assert(3 == (*ip1)); /* ip1 continues to point to the same item, not the same position */
ip1--;
assert(2 == (*ip1));
for (mse::ivector<int>::cipointer cip = v.cbegin(); v.cend() != cip; cip++) {
/* You might imagine what would happen if cip were a regular vector iterator. */
v.insert(v.begin(), (*cip));
}
/* Btw, the iterators are compatible with stl algorithms, like any other stl iterators. */
std::sort(v.begin(), v.end());
}
{
/*********************************/
/* us::msevector<>::ipointer */
/*********************************/
/* mse::us::msevector<> is another vector that is highly compatible with std::vector<>. But mse::us::msevector<> also
supports a new type of iterator called "ipointer". ipointers make more (intuitive) sense than standard vector
iterators. Upon insert or delete, ipointers continue to point to the same item, not (necessarily) the same
position. And they don't become "invalid" upon insert or delete, unless the item they point to is deleted. They
support all the standard iterator operators, but also have member functions with "friendlier" names. */
#ifdef MSVC2010_COMPATIBLE
int a1[4] = { 1, 2, 3, 4 };
mse::us::msevector<int> v1(a1, a1+4);
#else /*MSVC2010_COMPATIBLE*/
mse::us::msevector<int> v1 = { 1, 2, 3, 4 };
#endif /*MSVC2010_COMPATIBLE*/
mse::us::msevector<int> v = v1;
{
mse::us::msevector<int>::ipointer ip1 = v.ibegin();
ip1 += 2;
assert(3 == (*ip1));
auto ip2 = v.ibegin(); /* ibegin() returns an ipointer */
v.erase(ip2); /* remove the first item */
assert(3 == (*ip1)); /* ip1 continues to point to the same item, not the same position */
ip1--;
assert(2 == (*ip1));
for (mse::us::msevector<int>::cipointer cip = v.cibegin(); v.ciend() != cip; cip++) {
/* You might imagine what would happen if cip were a regular vector iterator. */
v.insert(v.ibegin(), (*cip));
}
}
v = v1;
{
/* This code block is equivalent to the previous code block, but uses ipointer's more "readable" interface
that might make the code a little more clear to those less familiar with C++ syntax. */
mse::us::msevector<int>::ipointer ip_vit1 = v.ibegin();
ip_vit1.advance(2);
assert(3 == ip_vit1.item());
auto ip_vit2 = v.ibegin();
v.erase(ip_vit2);
assert(3 == ip_vit1.item());
ip_vit1.set_to_previous();
assert(2 == ip_vit1.item());
mse::us::msevector<int>::cipointer cip(v);
for (cip.set_to_beginning(); cip.points_to_an_item(); cip.set_to_next()) {
v.insert_before(v.ibegin(), (*cip));
}
}
/* Btw, ipointers are compatible with stl algorithms, like any other stl iterators. */
std::sort(v.ibegin(), v.iend());
/* And just to be clear, mse::us::msevector<> retains its original (high performance) stl::vector iterators. */
std::sort(v.begin(), v.end());
/* mse::us::msevector<> also provides "safe" (bounds checked) versions of the original stl::vector iterators. */
std::sort(v.ss_begin(), v.ss_end());
{
/* A "scope" version of the safe iterators can be used when the vector is declared as a scope
object. There are limitations on when they can be used, but unlike the other msevector iterators,
those restrictions ensure that they won't be used to access the vector after it's been deallocated. */
mse::TXScopeObj<mse::us::msevector<int>> vector1_scpobj = mse::us::msevector<int>{ 1, 2, 3 };
{
auto scp_ss_iter1 = mse::make_xscope_begin_iterator(&vector1_scpobj);
auto scp_ss_iter2 = mse::make_xscope_end_iterator(&vector1_scpobj);
std::sort(scp_ss_iter1, scp_ss_iter2);
auto scp_ss_citer3 = mse::make_xscope_begin_const_iterator(&vector1_scpobj);
scp_ss_citer3 = scp_ss_iter1;
scp_ss_citer3 = mse::make_xscope_begin_const_iterator(&vector1_scpobj);
scp_ss_citer3 += 2;
auto res1 = *scp_ss_citer3;
auto res2 = scp_ss_citer3[0];
/* Here we demonstrate the case where the vector is a member of a class/struct declared as a
scope object. */
class CContainer1 {
public:
CContainer1() : m_vector({ 1, 2, 3 }) {}
mse::us::msevector<int> m_vector;
};
mse::TXScopeObj<CContainer1> container1_scpobj;
auto container1_m_vector_scpptr = mse::make_xscope_pointer_to_member_v2(&container1_scpobj, &CContainer1::m_vector);
auto scp_ss_citer4 = mse::make_xscope_begin_iterator(container1_m_vector_scpptr);
scp_ss_citer4++;
auto res3 = *scp_ss_citer4;
/* Note that scope iterators, while they exist, have the effect of "structure locking" their associated container.
That is, while a scope iterator exists, the "structure" (i.e. size or capacity) of the target container will remain
unchanged. Attempting any operation that would affect the structure would result in an exception. This property
allows us to (safely) obtain a (direct) scope pointer to the scope iterator's target element. */
auto scp_ptr1 = mse::xscope_pointer(scp_ss_iter1);
auto res4 = *scp_ptr1;
}
/* After all the scope pointers have gone out of scope, you may again perform operations that affect the container's
"structure" (i.e. size or capacity). */
vector1_scpobj.push_back(4);
}
}
{
/*********************/
/* mstd::array<> */
/*********************/
/* mse::mstd::array<> is an almost "completely safe" (bounds checked, iterator checked and "lifespan aware")
implementation of std::array. */
/* Here we demonstrate some iterator safety. */
mse::mstd::array<int, 3> a1 = { 1, 2, 3 };
mse::mstd::array<int, 3> a2 = { 11, 12, 13 };
#ifndef MSE_MSTDARRAY_DISABLED
MSE_TRY {
for (auto it1 = a1.begin(); it1 != a2.end(); it1++) {
/* It's not going to make it here. The invalid iterator comparison will throw an exception. */
std::cerr << "unexpected execution" << std::endl;
}
}
MSE_CATCH_ANY {
std::cerr << "expected exception" << std::endl;
}
#ifndef EXCLUDE_DUE_TO_MSVC2019_INTELLISENSE_BUGS1
/* Here we're demonstrating mse::mstd::array<> and its iterator's "lifespan awareness". */
mse::mstd::array<int, 2>::iterator it1;
{
mse::mstd::array<int, 2> a3 = { 5 }; /*Notice that initializer lists may contain fewer elements than the array.*/
it1 = a3.begin();
assert(5 == (*it1));
}
MSE_TRY{
/* it1 "knows" that its target has been destroyed. It will throw an exception on any attempt to dereference it. */
int i = (*it1);
std::cerr << "unexpected execution" << std::endl;
}
MSE_CATCH_ANY{
std::cerr << "expected exception" << std::endl;
}
#endif // !EXCLUDE_DUE_TO_MSVC2019_INTELLISENSE_BUGS1
#endif // !MSE_MSTDARRAY_DISABLED
/* And of course the iterators can be used with the standard algorithms, just like those of std::array. */
std::sort(a2.begin(), a2.end());
{
/* If the array is declared as a "scope" object (which basically indicates that it is declared
on the stack), then you can use "scope" iterators. While there are limitations on when they can
be used, scope iterators would be the preferred iterator type where performance is a priority
as they don't require extra run-time overhead to ensure that the array has not been prematurely
deallocated. */
/* Here we're declaring an array as a scope object. */
mse::TXScopeObj<mse::mstd::array<int, 3>> array1_scpobj = mse::mstd::array<int, 3>{ 1, 2, 3 };
// or alternatively:
//auto array1_scpobj = mse::make_xscope(mse::mstd::array<int, 3>{ 1, 2, 3 });
/* Here we're obtaining a scope iterator to the array. */
auto scp_array_iter1 = mse::mstd::make_xscope_begin_iterator(&array1_scpobj);
auto scp_array_iter2 = mse::mstd::make_xscope_end_iterator(&array1_scpobj);
std::sort(scp_array_iter1, scp_array_iter2);
auto scp_array_citer3 = mse::mstd::make_xscope_begin_const_iterator(&array1_scpobj);
scp_array_citer3 = scp_array_iter1;
scp_array_citer3 = mse::mstd::make_xscope_begin_const_iterator(&array1_scpobj);
scp_array_citer3 += 2;
auto res1 = *scp_array_citer3;
auto res2 = scp_array_citer3[0];
/* Here we demonstrate the case where the array is a member of a class/struct declared as a
scope object. */
class CContainer1 {
public:
CContainer1() : m_array{ 1, 2, 3 } {}
mse::mstd::array<int, 3> m_array/* = { 1, 2, 3 }*/;
};
mse::TXScopeObj<CContainer1> container1_scpobj;
auto container1_m_array_scpptr = mse::make_xscope_pointer_to_member_v2(&container1_scpobj, &CContainer1::m_array);
auto scp_iter4 = mse::mstd::make_xscope_begin_iterator(container1_m_array_scpptr);
scp_iter4++;
auto res3 = *scp_iter4;
/* You can also obtain a corresponding scope pointer from a scope iterator. */
auto scp_ptr1 = mse::xscope_pointer(scp_iter4);
auto res4 = *scp_ptr1;
auto scp_cptr1 = mse::xscope_const_pointer(scp_iter4);
auto res5 = *scp_cptr1;
}
mse::mstd::array_test testobj1;
testobj1.test1();
}
{
/**********************/
/* us::msearray<> */
/**********************/
/* mse::us::msearray<> is another array implementation that's not quite as safe as mse::mstd::array<> in the sense
that its iterators are not "lifespan aware" (i.e. could be used to access an array after it's been deallocated).
And it provides both "safe" (bounds-checked) and unsafe iterators. Basically, mse::us::msearray<> is a compromise
between performance and safety. */
mse::us::msearray<int, 3> a1 = { 1, 2, 3 };
mse::us::msearray<int, 3> a2 = { 11, 12, 13 };
//bool bres1 = (a1.begin() == a2.end());
/* The previous commented out line would result in "undefined behavior. */
MSE_TRY {
/* The behavior of the next line is not "undefined". It's going to throw an exception. */
bool bres2 = (a1.ss_begin() == a2.ss_end());
}
MSE_CATCH_ANY {
std::cerr << "expected exception" << std::endl;
}
auto ss_cit1 = a1.ss_cbegin();
/* These safe iterators support traditional and "friendly" iterator operation syntax. */
ss_cit1++;
ss_cit1.set_to_next(); /*same as previous line*/
ss_cit1.set_to_beginning();
bool bres3 = ss_cit1.has_previous();
ss_cit1.set_to_end_marker();
bool bres4 = ss_cit1.points_to_an_item();
std::sort(a2.ss_begin(), a2.ss_end());
{
/* A "scope" version of the safe iterators can be used when the array is declared as a scope
object. There are limitations on when they can be used, but unlike the other msearray iterators,
those restrictions ensure that they won't be used to access the array after it's been deallocated. */
mse::TXScopeObj<mse::us::msearray<int, 3>> array1_scpobj = mse::us::msearray<int, 3>{ 1, 2, 3 };
auto scp_ss_iter1 = mse::make_xscope_begin_iterator(&array1_scpobj);
auto scp_ss_iter2 = mse::make_xscope_end_iterator(&array1_scpobj);
std::sort(scp_ss_iter1, scp_ss_iter2);
auto scp_ss_citer3 = mse::make_xscope_begin_const_iterator(&array1_scpobj);
scp_ss_citer3 = scp_ss_iter1;
scp_ss_citer3 = mse::make_xscope_begin_const_iterator(&array1_scpobj);
scp_ss_citer3 += 2;
auto res1 = *scp_ss_citer3;
auto res2 = scp_ss_citer3[0];
/* Here we demonstrate the case where the array is a member of a class/struct declared as a
scope object. */
class CContainer1 {
public:
CContainer1() : m_array({ 1, 2, 3 }) {}
mse::us::msearray<int, 3> m_array;
};
mse::TXScopeObj<CContainer1> container1_scpobj;
auto container1_m_array_scpptr = mse::make_xscope_pointer_to_member_v2(&container1_scpobj, &CContainer1::m_array);
auto scp_ss_citer4 = mse::make_xscope_begin_iterator(container1_m_array_scpptr);
scp_ss_citer4++;
auto res3 = *scp_ss_citer4;
/* You can also obtain a corresponding scope pointer from a scope iterator. */
auto scp_ptr1 = mse::xscope_pointer(scp_ss_citer4);
auto res4 = *scp_ptr1;
auto scp_cptr1 = mse::xscope_const_pointer(scp_ss_citer4);
auto res5 = *scp_cptr1;
}
mse::msearray_test testobj1;
testobj1.test1();
}
{
/*******************************/
/* CInt, CSize_t and CBool */
/*******************************/
/* The unsigned types like size_t can cause insidious bugs due to the fact that they can cause signed integers to be
implicitly converted to unsigned. msetl provides substitutes for size_t and int that change the implicit conversion to
instead be from unsigned to signed. */
mse::self_test::CPrimitivesTest1::s_test1();
{
size_t number_of_security_credits = 0;
number_of_security_credits += 5;
int minimum_number_of_security_credits_required_for_access = 7;
bool access_granted = false;
if (number_of_security_credits - minimum_number_of_security_credits_required_for_access >= 0) {
/* You may not even get a compiler warning about the implicit conversion from (signed) int to (unsigned) size_t. */
access_granted = true; /*oops*/
}
else {
access_granted = false;
assert(false);
}
}
{
mse::CNDSize_t number_of_security_credits = 0;
number_of_security_credits += 5;
int minimum_number_of_security_credits_required_for_access = 7;
bool access_granted = false;
if (number_of_security_credits - minimum_number_of_security_credits_required_for_access >= 0) {
access_granted = true;
assert(false);
}
else {
access_granted = false; /* that's better */
}
}
{
size_t number_of_security_credits = 0;
number_of_security_credits += 5;
mse::CNDInt minimum_number_of_security_credits_required_for_access = 7;
mse::CNDBool access_granted = false;
if (number_of_security_credits - minimum_number_of_security_credits_required_for_access >= 0) {
access_granted = true;
assert(false);
}
else {
access_granted = false; /* this works too */
}
}
mse::CSize_t mse_szt1 = 0;
/* size_t szt2 = mse_szt1; */ /* This wouldn't compile. */
size_t szt1 = mse::as_a_size_t(mse_szt1); /* We exclude automatic conversion from mse::CSize_t to size_t because we
consider size_t an intrinsically error prone type. */
#ifndef MSVC2010_COMPATIBLE
size_t szt2 = static_cast<size_t>(mse_szt1); /* We exclude automatic conversion from mse::CSize_t to size_t because we
consider size_t an intrinsically error prone type. */
#endif // !MSVC2010_COMPATIBLE
MSE_TRY {
mse::CNDSize_t mse_szt2 = 0;
mse_szt2 = -3;
}
MSE_CATCH_ANY {
std::cerr << "expected exception" << std::endl;
/* The exception is triggered by an "out of range" assignment to an mse::CSize_t. */
}
MSE_TRY {
mse::CSize_t mse_szt3 = 3;
mse_szt3 -= 1; /* this is fine */
mse_szt3 -= 4; /* this is gonna throw an exception */
}
MSE_CATCH_ANY {
std::cerr << "expected exception" << std::endl;
/* The exception is triggered by an attempt to set an mse::CSize_t to an "out of range" value. */
}
}
{
/**************************/
/* TRegisteredPointer */
/**************************/
/* For safety reasons we want to avoid using native pointers. "Managed" pointers like std:shared_ptr are an alternative, but
sometimes you don't want a pointer that takes ownership (of the object's lifespan). So we provide mse::TRegisteredPointer.
Because it doesn't take ownership, it can be used with objects allocated on the stack and is compatible with raii
techniques. Also, in most cases, it can be used as a compatible, direct substitute for native pointers, making it
straightforward to update legacy code. Proper "const", "not null" and "fixed" (non-retargetable) versions are provided as
well.*/
class A {
public:
virtual ~A() {}
int b = 3;
};
class B {
public:
static int foo1(A* a_native_ptr) { return a_native_ptr->b; }
static int foo2(mse::TRegisteredPointer<A> A_registered_ptr) { return A_registered_ptr->b; }
/* mse::TRegisteredFixedConstPointer<A> is recommended where you might have used "const A&".*/
static int foo3(mse::TRegisteredFixedConstPointer<A> A_registered_fc_ptr) { return A_registered_fc_ptr->b; }
};
A* A_native_ptr = nullptr;
/* mse::TRegisteredPointer<> is basically a "safe" version of the native pointer. */
mse::TRegisteredPointer<A> A_registered_ptr1;
{
A a;
mse::TRegisteredObj<A> registered_a;
/* mse::TRegisteredObj<A> is a class that is publicly derived from A, and so should be a compatible substitute for A
in almost all cases. */
/* You can also use the make_registered() function to obtain a registered object from a given value. */
auto registered_a2 = mse::make_registered(A());
assert(a.b == registered_a.b);
A_native_ptr = &a;
A_registered_ptr1 = ®istered_a;
assert(A_native_ptr->b == A_registered_ptr1->b);
mse::TRegisteredPointer<A> A_registered_ptr2 = ®istered_a;
A_registered_ptr2 = nullptr;
#ifndef MSE_REGISTEREDPOINTER_DISABLED
MSE_TRY {
int i = A_registered_ptr2->b; /* this is gonna throw an exception */
}
MSE_CATCH_ANY {
std::cerr << "expected exception" << std::endl;
/* The exception is triggered by an attempt to dereference a null "registered pointer". */
}
#endif // !MSE_REGISTEREDPOINTER_DISABLED
B::foo3(®istered_a);
/* mse::TRegisteredPointers don't convert directly into mse::TRegisteredFixedConstPointers because
mse::TRegisteredFixedConstPointers are never supposed to be null, where mse::TRegisteredPointers can be. But you
can easily obtain an mse::TRegisteredFixedPointer which does convert to an mse::TRegisteredFixedConstPointer. */
B::foo3(&*A_registered_ptr1);
/* Functions can be templated to allow the caller to use the (smart/safe) pointer of their choice. */
H::foo4<mse::TRegisteredFixedConstPointer<A>>(&*A_registered_ptr1);
/* You don't actually need to explicitly specify the template type. */
H::foo4(&*A_registered_ptr1);
if (A_registered_ptr2) {
assert(false);
}
else if (A_registered_ptr2 != A_registered_ptr1) {
A_registered_ptr2 = A_registered_ptr1;
assert(A_registered_ptr2 == A_registered_ptr1);
}
}
#ifndef MSE_REGISTEREDPOINTER_DISABLED
MSE_TRY {
/* A_registered_ptr1 "knows" that the (registered) object it was pointing to has now been deallocated. */
int i = A_registered_ptr1->b; /* So this is gonna throw an exception */
}
MSE_CATCH_ANY {
std::cerr << "expected exception" << std::endl;
}
#endif // !MSE_REGISTEREDPOINTER_DISABLED
{
/* For heap allocations mse::registered_new is kind of analagous to std::make_shared, but again,
mse::TRegisteredPointers don't take ownership so you are responsible for deallocation. */
auto A_registered_ptr3 = mse::registered_new<A>();
assert(3 == A_registered_ptr3->b);
mse::registered_delete<A>(A_registered_ptr3);
#ifndef MSE_REGISTEREDPOINTER_DISABLED
MSE_TRY {
/* A_registered_ptr3 "knows" that the (registered) object it was pointing to has now been deallocated. */
int i = A_registered_ptr3->b; /* So this is gonna throw an exception */
}
MSE_CATCH_ANY {
std::cerr << "expected exception" << std::endl;
}
#endif // !MSE_REGISTEREDPOINTER_DISABLED
}
{
/* Remember that registered pointers can only point to registered objects. So, for example, if you want
a registered pointer to an object's base class object, that base class object has to be a registered
object. */
class DA : public mse::TRegisteredObj<A> {};
mse::TRegisteredObj<DA> registered_da;
mse::TRegisteredPointer<DA> DA_registered_ptr1 = ®istered_da;
mse::TRegisteredPointer<A> A_registered_ptr4 = DA_registered_ptr1;
A_registered_ptr4 = ®istered_da;
class D : public A {};
mse::TRegisteredObj<D> registered_d;
mse::TRegisteredPointer<D> D_registered_ptr1 = ®istered_d;
/* The next commented out line of code is not going to work because D's base class object is not a
registered object. */
//mse::TRegisteredPointer<A> A_registered_ptr5 = D_registered_ptr1;
}
{
/* Obtaining safe pointers to members of registered objects: */
class E {
public:
virtual ~E() {}
mse::TRegisteredObj<std::string> reg_s = "some text ";
std::string s2 = "some other text ";
};
mse::TRegisteredObj<E> registered_e;
mse::TRegisteredPointer<E> E_registered_ptr1 = ®istered_e;
/* To obtain a safe pointer to a member of a registered object you could just make the
member itself a registered object. */
mse::TRegisteredPointer<std::string> reg_s_registered_ptr1 = &(E_registered_ptr1->reg_s);
/* Or you can use the "mse::make_pointer_to_member_v2()" function. */
auto s2_safe_ptr1 = mse::make_pointer_to_member_v2(E_registered_ptr1, &E::s2);
(*s2_safe_ptr1) = "some new text";
auto s2_safe_const_ptr1 = mse::make_const_pointer_to_member_v2(E_registered_ptr1, &E::s2);
/* The return type of mse::make_pointer_to_member_v2() depends on the type of the parameters passed
to it. In this case, the type of s2_safe_ptr1 is mse::us::TSyncWeakFixedPointer<std::string,
mse::TRegisteredPointer<E>>. s2_safe_ptr1 here is essentially a pointer to "E.s2"
(string member of class E) with a registered pointer to E to in its pocket. It uses the registered
pointer to ensure that it is safe to access the object. */
/* In practice, rather than declaring a specific mse::us::TSyncWeakFixedPointer parameter, we expect
functions intended for general use to be "templatized" so that they can accept any type of pointer. */
std::string res1 = H::foo6(s2_safe_ptr1, s2_safe_const_ptr1);
}
{
/***************************/
/* TCRegisteredPointer */
/***************************/
class C;
class D {
public:
virtual ~D() {}
mse::TCRegisteredPointer<C> m_c_ptr;
};
class C {
public:
mse::TCRegisteredPointer<D> m_d_ptr;
};
mse::TCRegisteredObj<C> regobjfl_c;
mse::TCRegisteredPointer<D> d_ptr = mse::cregistered_new<D>();
regobjfl_c.m_d_ptr = d_ptr;
d_ptr->m_c_ptr = ®objfl_c;
mse::cregistered_delete<D>(d_ptr);
{
/* Polymorphic conversions. */
class FD : public mse::TCRegisteredObj<D> {};
mse::TCRegisteredObj<FD> cregistered_fd;
mse::TCRegisteredPointer<FD> FD_cregistered_ptr1 = &cregistered_fd;
mse::TCRegisteredPointer<D> D_cregistered_ptr4 = FD_cregistered_ptr1;
D_cregistered_ptr4 = &cregistered_fd;
mse::TCRegisteredFixedPointer<D> D_cregistered_fptr1 = &cregistered_fd;
mse::TCRegisteredFixedConstPointer<D> D_cregistered_fcptr1 = &cregistered_fd;
}
}
mse::self_test::CRegPtrTest1::s_test1();
mse::self_test::CCRegPtrTest1::s_test1();
}
{
/*********************/
/* TNoradPointer */
/*********************/
/* mse::TNoradPointer<>, like mse::TCRegisteredPointer<>, behaves similar to native pointers. But where registered
pointers are automatically set to nullptr when their target is destroyed, the destruction of an object while a "norad"
pointer is targeting it results in program termination. This drastic consequence allows norad pointers' run-time
safety mechanism to be very lightweight (compared to that of registered pointers).
*/
class C;
class D {
public:
virtual ~D() {}
mse::TNoradPointer<C> m_c_ptr;
};
class C {
public:
mse::TNoradPointer<D> m_d_ptr;
};
mse::TNoradObj<C> noradobj_c;
mse::TNoradPointer<D> d_ptr = mse::norad_new<D>();
noradobj_c.m_d_ptr = d_ptr;
d_ptr->m_c_ptr = &noradobj_c;
/* We must make sure that there are no other references to the target of d_ptr before deleting it. Registered pointers don't
have the same requirement. */
noradobj_c.m_d_ptr = nullptr;
mse::norad_delete<D>(d_ptr);
/* You can also use the make_norad() function to obtain a norad object from a given value. */
auto noradobj_c2 = mse::make_norad(C());
{
/* Polymorphic conversions. */
class FD : public mse::TNoradObj<D> {};
mse::TNoradObj<FD> norad_fd;
mse::TNoradPointer<FD> FD_norad_ptr1 = &norad_fd;
mse::TNoradPointer<D> D_norad_ptr4 = FD_norad_ptr1;
D_norad_ptr4 = &norad_fd;
mse::TNoradFixedPointer<D> D_norad_fptr1 = &norad_fd;
mse::TNoradFixedConstPointer<D> D_norad_fcptr1 = &norad_fd;
}
}
mse::self_test::CNoradPtrTest1::s_test1();
#if defined(MSEREGISTEREDREFWRAPPER) && !defined(MSE_PRIMITIVES_DISABLED)
{
/*****************************/
/* TRegisteredRefWrapper */
/*****************************/
/* Stl provides a copyable, assignable wrapper for C++ references called std::reference_wrapper. std::reference_wrappers,
like native C++ references, are not completely safe in the sense that the object they refer to can be deallocated while
a reference to it is still available. So we provide mse::TRegisteredRefWrapper, a safe implementation of
std::reference_wrapper that "knows" when the object being referenced has been deallocated and will throw an exception
on any attempt to access the object after it has been destroyed.
In most cases it is probably preferable to just use mse::TRegisteredFixedPointer instead of mse::TRegisteredRefWrapper.
*/
{
#ifndef __apple_build_version__
/* This example originally comes from http://en.cppreference.com/w/cpp/utility/functional/reference_wrapper. */
std::list<mse::TRegisteredObj<mse::CInt>> l(10);
std::iota(l.begin(), l.end(), -4);
std::vector<mse::TRegisteredRefWrapper<mse::CInt>> v(l.begin(), l.end());
// can't use shuffle on a list (requires random access), but can use it on a vector
std::shuffle(v.begin(), v.end(), std::mt19937{ std::random_device{}() });
std::cout << '\n';
std::cout << "TRegisteredRefWrapper test output: \n";
std::cout << "Contents of the list: ";
for (auto n : l) { std::cout << n << ' '; } std::cout << '\n';
std::cout << "Contents of the list, as seen through a shuffled vector: ";
for (auto i : v) { std::cout << static_cast<mse::CInt&>(i) << ' '; } std::cout << '\n';
std::cout << "Doubling the values in the initial list...\n";
for (auto& i : l) {
i *= 2;
}
std::cout << "Contents of the list, as seen through a shuffled vector: ";
for (auto i : v) { std::cout << static_cast<mse::CInt&>(i) << ' '; } std::cout << '\n';