rice 4.0.4 → 4.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +30 -0
- data/Rakefile +1 -1
- data/include/rice/rice.hpp +2596 -1771
- data/include/rice/stl.hpp +1580 -271
- data/lib/mkmf-rice.rb +5 -2
- data/lib/version.rb +1 -1
- data/rice/Arg.hpp +6 -6
- data/rice/Arg.ipp +8 -9
- data/rice/Constructor.hpp +2 -2
- data/rice/Data_Object.ipp +69 -15
- data/rice/Data_Object_defn.hpp +1 -15
- data/rice/Data_Type.ipp +56 -86
- data/rice/Data_Type_defn.hpp +14 -17
- data/rice/Director.hpp +0 -1
- data/rice/Enum.ipp +31 -22
- data/rice/Exception.ipp +2 -3
- data/rice/Exception_defn.hpp +5 -5
- data/rice/HandlerRegistration.hpp +15 -0
- data/rice/Return.hpp +5 -4
- data/rice/Return.ipp +8 -3
- data/rice/detail/ExceptionHandler.hpp +8 -0
- data/rice/detail/ExceptionHandler.ipp +28 -0
- data/rice/detail/{Exception_Handler_defn.hpp → ExceptionHandler_defn.hpp} +17 -21
- data/rice/detail/HandlerRegistry.hpp +51 -0
- data/rice/detail/HandlerRegistry.ipp +20 -0
- data/rice/detail/InstanceRegistry.hpp +34 -0
- data/rice/detail/InstanceRegistry.ipp +50 -0
- data/rice/detail/MethodInfo.ipp +1 -1
- data/rice/detail/NativeAttribute.hpp +26 -15
- data/rice/detail/NativeAttribute.ipp +76 -47
- data/rice/detail/NativeFunction.hpp +60 -13
- data/rice/detail/NativeFunction.ipp +103 -85
- data/rice/detail/NativeIterator.hpp +49 -0
- data/rice/detail/NativeIterator.ipp +102 -0
- data/rice/detail/NativeRegistry.hpp +31 -0
- data/rice/detail/{method_data.ipp → NativeRegistry.ipp} +20 -16
- data/rice/detail/Registries.hpp +26 -0
- data/rice/detail/Registries.ipp +23 -0
- data/rice/detail/RubyFunction.hpp +6 -11
- data/rice/detail/RubyFunction.ipp +10 -22
- data/rice/detail/Type.hpp +1 -1
- data/rice/detail/Type.ipp +2 -2
- data/rice/detail/TypeRegistry.hpp +8 -11
- data/rice/detail/TypeRegistry.ipp +3 -28
- data/rice/detail/Wrapper.hpp +0 -2
- data/rice/detail/Wrapper.ipp +73 -23
- data/rice/detail/cpp_protect.hpp +93 -0
- data/rice/detail/default_allocation_func.ipp +1 -1
- data/rice/detail/from_ruby.ipp +206 -2
- data/rice/detail/to_ruby.ipp +39 -5
- data/rice/detail/to_ruby_defn.hpp +1 -1
- data/rice/forward_declares.ipp +6 -0
- data/rice/global_function.hpp +0 -4
- data/rice/global_function.ipp +0 -6
- data/rice/rice.hpp +29 -24
- data/rice/stl.hpp +6 -1
- data/test/embed_ruby.cpp +0 -15
- data/test/test_Array.cpp +20 -24
- data/test/test_Class.cpp +8 -47
- data/test/test_Constructor.cpp +0 -2
- data/test/test_Data_Object.cpp +25 -11
- data/test/test_Data_Type.cpp +124 -28
- data/test/test_Director.cpp +12 -13
- data/test/test_Enum.cpp +65 -26
- data/test/test_Inheritance.cpp +9 -9
- data/test/test_Iterator.cpp +134 -5
- data/test/test_Keep_Alive.cpp +7 -7
- data/test/test_Memory_Management.cpp +1 -1
- data/test/test_Module.cpp +25 -62
- data/test/test_Object.cpp +66 -3
- data/test/test_Ownership.cpp +12 -13
- data/test/test_Self.cpp +12 -13
- data/test/test_Stl_Map.cpp +696 -0
- data/test/test_Stl_Optional.cpp +3 -3
- data/test/test_Stl_Pair.cpp +38 -2
- data/test/test_Stl_Reference_Wrapper.cpp +102 -0
- data/test/test_Stl_SmartPointer.cpp +5 -5
- data/test/test_Stl_Unordered_Map.cpp +697 -0
- data/test/test_Stl_Variant.cpp +301 -0
- data/test/test_Stl_Vector.cpp +200 -41
- data/test/test_Struct.cpp +3 -3
- data/test/test_To_From_Ruby.cpp +6 -0
- data/test/test_Tracking.cpp +239 -0
- data/test/unittest.hpp +13 -4
- metadata +23 -13
- data/rice/detail/Exception_Handler.hpp +0 -8
- data/rice/detail/Exception_Handler.ipp +0 -28
- data/rice/detail/Iterator.hpp +0 -23
- data/rice/detail/Iterator.ipp +0 -47
- data/rice/detail/function_traits.hpp +0 -124
- data/rice/detail/method_data.hpp +0 -29
- data/rice/detail/rice_traits.hpp +0 -116
- data/rice/ruby_try_catch.hpp +0 -86
data/include/rice/stl.hpp
CHANGED
@@ -49,6 +49,11 @@ namespace Rice::detail
|
|
49
49
|
{
|
50
50
|
}
|
51
51
|
|
52
|
+
bool is_convertible(VALUE value)
|
53
|
+
{
|
54
|
+
return rb_type(value) == RUBY_T_STRING;
|
55
|
+
}
|
56
|
+
|
52
57
|
std::string convert(VALUE value)
|
53
58
|
{
|
54
59
|
if (value == Qnil && this->arg_ && this->arg_->hasDefaultValue())
|
@@ -70,6 +75,11 @@ namespace Rice::detail
|
|
70
75
|
class From_Ruby<std::string*>
|
71
76
|
{
|
72
77
|
public:
|
78
|
+
bool is_convertible(VALUE value)
|
79
|
+
{
|
80
|
+
return rb_type(value) == RUBY_T_STRING;
|
81
|
+
}
|
82
|
+
|
73
83
|
std::string* convert(VALUE value)
|
74
84
|
{
|
75
85
|
detail::protect(rb_check_type, value, (int)T_STRING);
|
@@ -91,6 +101,11 @@ namespace Rice::detail
|
|
91
101
|
{
|
92
102
|
}
|
93
103
|
|
104
|
+
bool is_convertible(VALUE value)
|
105
|
+
{
|
106
|
+
return rb_type(value) == RUBY_T_STRING;
|
107
|
+
}
|
108
|
+
|
94
109
|
std::string& convert(VALUE value)
|
95
110
|
{
|
96
111
|
if (value == Qnil && this->arg_ && this->arg_->hasDefaultValue())
|
@@ -138,7 +153,7 @@ namespace Rice::detail
|
|
138
153
|
std::vector<VALUE> args(2);
|
139
154
|
args[0] = To_Ruby<T>().convert(data.real());
|
140
155
|
args[1] = To_Ruby<T>().convert(data.imag());
|
141
|
-
return protect(
|
156
|
+
return protect(rb_funcallv, rb_mKernel, rb_intern("Complex"), (int)args.size(), (const VALUE*)args.data());
|
142
157
|
}
|
143
158
|
};
|
144
159
|
|
@@ -148,8 +163,8 @@ namespace Rice::detail
|
|
148
163
|
public:
|
149
164
|
std::complex<T> convert(VALUE value)
|
150
165
|
{
|
151
|
-
VALUE real = protect(
|
152
|
-
VALUE imaginary = protect(
|
166
|
+
VALUE real = protect(rb_funcallv, value, rb_intern("real"), 0, (const VALUE*)nullptr);
|
167
|
+
VALUE imaginary = protect(rb_funcallv, value, rb_intern("imaginary"), 0, (const VALUE*)nullptr);
|
153
168
|
|
154
169
|
return std::complex<T>(From_Ruby<T>().convert(real), From_Ruby<T>().convert(imaginary));
|
155
170
|
}
|
@@ -161,8 +176,8 @@ namespace Rice::detail
|
|
161
176
|
public:
|
162
177
|
std::complex<T>& convert(VALUE value)
|
163
178
|
{
|
164
|
-
VALUE real = protect(
|
165
|
-
VALUE imaginary = protect(
|
179
|
+
VALUE real = protect(rb_funcallv, value, rb_intern("real"), 0, (const VALUE*)nullptr);
|
180
|
+
VALUE imaginary = protect(rb_funcallv, value, rb_intern("imaginary"), 0, (const VALUE*)nullptr);
|
166
181
|
this->converted_ = std::complex<T>(From_Ruby<T>().convert(real), From_Ruby<T>().convert(imaginary));
|
167
182
|
|
168
183
|
return this->converted_;
|
@@ -273,178 +288,52 @@ namespace Rice::detail
|
|
273
288
|
};
|
274
289
|
}
|
275
290
|
|
276
|
-
// =========
|
277
|
-
|
278
|
-
|
279
|
-
namespace Rice
|
280
|
-
{
|
281
|
-
template<typename T>
|
282
|
-
Data_Type<T> define_pair(std::string name);
|
283
|
-
|
284
|
-
template<typename T>
|
285
|
-
Data_Type<T> define_pair_under(Object module, std::string name);
|
286
|
-
}
|
287
|
-
|
291
|
+
// ========= reference_wrapper.hpp =========
|
288
292
|
|
289
|
-
// --------- pair.ipp ---------
|
290
293
|
|
291
|
-
|
292
|
-
#include <
|
293
|
-
#include <utility>
|
294
|
+
// --------- reference_wrapper.ipp ---------
|
295
|
+
#include <functional>
|
294
296
|
|
295
|
-
namespace Rice
|
297
|
+
namespace Rice::detail
|
296
298
|
{
|
297
|
-
namespace stl
|
298
|
-
{
|
299
|
-
template<typename T>
|
300
|
-
class PairHelper
|
301
|
-
{
|
302
|
-
public:
|
303
|
-
PairHelper(Data_Type<T> klass) : klass_(klass)
|
304
|
-
{
|
305
|
-
this->define_constructor();
|
306
|
-
this->define_copyable_methods();
|
307
|
-
this->define_access_methods();
|
308
|
-
this->define_modify_methods();
|
309
|
-
this->define_to_s();
|
310
|
-
}
|
311
|
-
|
312
|
-
private:
|
313
|
-
void define_constructor()
|
314
|
-
{
|
315
|
-
klass_.define_constructor(Constructor<T, typename T::first_type&, typename T::second_type&>());
|
316
|
-
}
|
317
|
-
|
318
|
-
void define_copyable_methods()
|
319
|
-
{
|
320
|
-
if constexpr (std::is_copy_constructible_v<typename T::first_type> && std::is_copy_constructible_v<typename T::second_type>)
|
321
|
-
{
|
322
|
-
klass_.define_method("copy", [](T& self) -> T
|
323
|
-
{
|
324
|
-
return self;
|
325
|
-
});
|
326
|
-
}
|
327
|
-
else
|
328
|
-
{
|
329
|
-
klass_.define_method("copy", [](T& self) -> T
|
330
|
-
{
|
331
|
-
throw std::runtime_error("Cannot copy pair with non-copy constructible types");
|
332
|
-
return self;
|
333
|
-
});
|
334
|
-
}
|
335
|
-
}
|
336
|
-
|
337
|
-
void define_access_methods()
|
338
|
-
{
|
339
|
-
// Access methods
|
340
|
-
klass_.define_method("first", [](T& self) -> typename T::first_type&
|
341
|
-
{
|
342
|
-
return self.first;
|
343
|
-
})
|
344
|
-
.define_method("second", [](T& self) -> typename T::second_type&
|
345
|
-
{
|
346
|
-
return self.second;
|
347
|
-
});
|
348
|
-
}
|
349
|
-
|
350
|
-
void define_modify_methods()
|
351
|
-
{
|
352
|
-
// Access methods
|
353
|
-
klass_.define_method("first=", [](T& self, typename T::first_type& value) -> typename T::first_type&
|
354
|
-
{
|
355
|
-
self.first = value;
|
356
|
-
return self.first;
|
357
|
-
})
|
358
|
-
.define_method("second=", [](T& self, typename T::second_type& value) -> typename T::second_type&
|
359
|
-
{
|
360
|
-
self.second = value;
|
361
|
-
return self.second;
|
362
|
-
});
|
363
|
-
}
|
364
|
-
|
365
|
-
void define_to_s()
|
366
|
-
{
|
367
|
-
if constexpr (detail::is_ostreamable_v<typename T::first_type> && detail::is_ostreamable_v<typename T::second_type>)
|
368
|
-
{
|
369
|
-
klass_.define_method("to_s", [](const T& self)
|
370
|
-
{
|
371
|
-
std::stringstream stream;
|
372
|
-
stream << "[" << self.first << ", " << self.second << "]";
|
373
|
-
return stream.str();
|
374
|
-
});
|
375
|
-
}
|
376
|
-
else
|
377
|
-
{
|
378
|
-
klass_.define_method("to_s", [](const T& self)
|
379
|
-
{
|
380
|
-
return "[Not printable]";
|
381
|
-
});
|
382
|
-
}
|
383
|
-
}
|
384
|
-
|
385
|
-
private:
|
386
|
-
Data_Type<T> klass_;
|
387
|
-
};
|
388
|
-
} // namespace
|
389
|
-
|
390
299
|
template<typename T>
|
391
|
-
|
300
|
+
struct Type<std::reference_wrapper<T>>
|
392
301
|
{
|
393
|
-
|
302
|
+
constexpr static bool verify()
|
394
303
|
{
|
395
|
-
return
|
304
|
+
return Type<T>::verify();
|
396
305
|
}
|
397
|
-
|
398
|
-
Data_Type<T> result = define_class_under<detail::intrinsic_type<T>>(module, name.c_str());
|
399
|
-
stl::PairHelper helper(result);
|
400
|
-
return result;
|
401
|
-
}
|
306
|
+
};
|
402
307
|
|
403
308
|
template<typename T>
|
404
|
-
|
309
|
+
class To_Ruby<std::reference_wrapper<T>>
|
405
310
|
{
|
406
|
-
|
311
|
+
public:
|
312
|
+
VALUE convert(std::reference_wrapper<T>& data, bool takeOwnership = false)
|
407
313
|
{
|
408
|
-
return
|
314
|
+
return To_Ruby<T&>().convert(data.get());
|
409
315
|
}
|
410
|
-
|
411
|
-
Data_Type<T> result = define_class<detail::intrinsic_type<T>>(name.c_str());
|
412
|
-
stl::PairHelper<T> helper(result);
|
413
|
-
return result;
|
414
|
-
}
|
316
|
+
};
|
415
317
|
|
416
318
|
template<typename T>
|
417
|
-
|
418
|
-
{
|
419
|
-
std::string klassName = detail::makeClassName(typeid(T));
|
420
|
-
Module rb_mRice = define_module("Rice");
|
421
|
-
Module rb_mpair = define_module_under(rb_mRice, "Std");
|
422
|
-
return define_pair_under<T>(rb_mpair, klassName);
|
423
|
-
}
|
424
|
-
|
425
|
-
namespace detail
|
319
|
+
class From_Ruby<std::reference_wrapper<T>>
|
426
320
|
{
|
427
|
-
|
428
|
-
|
321
|
+
public:
|
322
|
+
bool is_convertible(VALUE value)
|
429
323
|
{
|
430
|
-
|
431
|
-
|
432
|
-
Type<T1>::verify();
|
433
|
-
Type<T2>::verify();
|
324
|
+
return true;
|
325
|
+
}
|
434
326
|
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
327
|
+
std::reference_wrapper<T> convert(VALUE value)
|
328
|
+
{
|
329
|
+
return this->converter_.convert(value);
|
330
|
+
}
|
439
331
|
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
}
|
332
|
+
private:
|
333
|
+
From_Ruby<T&> converter_;
|
334
|
+
};
|
444
335
|
}
|
445
336
|
|
446
|
-
|
447
|
-
|
448
337
|
// ========= smart_ptr.hpp =========
|
449
338
|
|
450
339
|
|
@@ -455,6 +344,7 @@ namespace Rice::detail
|
|
455
344
|
{
|
456
345
|
public:
|
457
346
|
WrapperSmartPointer(SmartPointer_T<Arg_Ts...>& data);
|
347
|
+
~WrapperSmartPointer();
|
458
348
|
void* get() override;
|
459
349
|
SmartPointer_T<Arg_Ts...>& data();
|
460
350
|
|
@@ -478,6 +368,12 @@ namespace Rice::detail
|
|
478
368
|
{
|
479
369
|
}
|
480
370
|
|
371
|
+
template <template <typename, typename...> typename SmartPointer_T, typename...Arg_Ts>
|
372
|
+
inline WrapperSmartPointer<SmartPointer_T, Arg_Ts...>::~WrapperSmartPointer()
|
373
|
+
{
|
374
|
+
Registries::instance.instances.remove(this->get());
|
375
|
+
}
|
376
|
+
|
481
377
|
template <template <typename, typename...> typename SmartPointer_T, typename...Arg_Ts>
|
482
378
|
inline void* WrapperSmartPointer<SmartPointer_T, Arg_Ts...>::get()
|
483
379
|
{
|
@@ -497,7 +393,7 @@ namespace Rice::detail
|
|
497
393
|
public:
|
498
394
|
VALUE convert(std::unique_ptr<T>& data)
|
499
395
|
{
|
500
|
-
std::pair<VALUE, rb_data_type_t*> rubyTypeInfo = detail::
|
396
|
+
std::pair<VALUE, rb_data_type_t*> rubyTypeInfo = detail::Registries::instance.types.figureType<T>(*data);
|
501
397
|
|
502
398
|
// Use custom wrapper type
|
503
399
|
using Wrapper_T = WrapperSmartPointer<std::unique_ptr, T>;
|
@@ -511,7 +407,7 @@ namespace Rice::detail
|
|
511
407
|
public:
|
512
408
|
std::unique_ptr<T>& convert(VALUE value)
|
513
409
|
{
|
514
|
-
Wrapper* wrapper = detail::getWrapper(value, Data_Type<T>::
|
410
|
+
Wrapper* wrapper = detail::getWrapper(value, Data_Type<T>::ruby_data_type());
|
515
411
|
|
516
412
|
using Wrapper_T = WrapperSmartPointer<std::unique_ptr, T>;
|
517
413
|
Wrapper_T* smartWrapper = dynamic_cast<Wrapper_T*>(wrapper);
|
@@ -540,7 +436,7 @@ namespace Rice::detail
|
|
540
436
|
public:
|
541
437
|
VALUE convert(std::shared_ptr<T>& data)
|
542
438
|
{
|
543
|
-
std::pair<VALUE, rb_data_type_t*> rubyTypeInfo = detail::
|
439
|
+
std::pair<VALUE, rb_data_type_t*> rubyTypeInfo = detail::Registries::instance.types.figureType<T>(*data);
|
544
440
|
|
545
441
|
// Use custom wrapper type
|
546
442
|
using Wrapper_T = WrapperSmartPointer<std::shared_ptr, T>;
|
@@ -554,7 +450,7 @@ namespace Rice::detail
|
|
554
450
|
public:
|
555
451
|
std::shared_ptr<T> convert(VALUE value)
|
556
452
|
{
|
557
|
-
Wrapper* wrapper = detail::getWrapper(value, Data_Type<T>::
|
453
|
+
Wrapper* wrapper = detail::getWrapper(value, Data_Type<T>::ruby_data_type());
|
558
454
|
|
559
455
|
using Wrapper_T = WrapperSmartPointer<std::shared_ptr, T>;
|
560
456
|
Wrapper_T* smartWrapper = dynamic_cast<Wrapper_T*>(wrapper);
|
@@ -573,7 +469,7 @@ namespace Rice::detail
|
|
573
469
|
public:
|
574
470
|
std::shared_ptr<T>& convert(VALUE value)
|
575
471
|
{
|
576
|
-
Wrapper* wrapper = detail::getWrapper(value, Data_Type<T>::
|
472
|
+
Wrapper* wrapper = detail::getWrapper(value, Data_Type<T>::ruby_data_type());
|
577
473
|
|
578
474
|
using Wrapper_T = WrapperSmartPointer<std::shared_ptr, T>;
|
579
475
|
Wrapper_T* smartWrapper = dynamic_cast<Wrapper_T*>(wrapper);
|
@@ -596,66 +492,1464 @@ namespace Rice::detail
|
|
596
492
|
};
|
597
493
|
}
|
598
494
|
|
599
|
-
// =========
|
495
|
+
// ========= monostate.hpp =========
|
600
496
|
|
601
497
|
|
602
|
-
|
498
|
+
// --------- monostate.ipp ---------
|
499
|
+
#include <variant>
|
500
|
+
|
501
|
+
namespace Rice::detail
|
603
502
|
{
|
604
|
-
template
|
605
|
-
|
503
|
+
template<>
|
504
|
+
struct Type<std::monostate>
|
505
|
+
{
|
506
|
+
constexpr static bool verify()
|
507
|
+
{
|
508
|
+
return true;
|
509
|
+
}
|
510
|
+
};
|
606
511
|
|
607
|
-
template
|
608
|
-
|
512
|
+
template<>
|
513
|
+
class To_Ruby<std::monostate>
|
514
|
+
{
|
515
|
+
public:
|
516
|
+
VALUE convert(std::monostate& _)
|
517
|
+
{
|
518
|
+
return Qnil;
|
519
|
+
}
|
520
|
+
};
|
521
|
+
|
522
|
+
template<>
|
523
|
+
class To_Ruby<std::monostate&>
|
524
|
+
{
|
525
|
+
public:
|
526
|
+
static VALUE convert(std::monostate& data, bool takeOwnership = false)
|
527
|
+
{
|
528
|
+
return Qnil;
|
529
|
+
}
|
530
|
+
};
|
531
|
+
|
532
|
+
template<>
|
533
|
+
class From_Ruby<std::monostate>
|
534
|
+
{
|
535
|
+
public:
|
536
|
+
bool is_convertible(VALUE value)
|
537
|
+
{
|
538
|
+
return false;
|
539
|
+
}
|
540
|
+
|
541
|
+
std::monostate convert(VALUE value)
|
542
|
+
{
|
543
|
+
return std::monostate();
|
544
|
+
}
|
545
|
+
};
|
546
|
+
|
547
|
+
template<>
|
548
|
+
class From_Ruby<std::monostate&>
|
549
|
+
{
|
550
|
+
public:
|
551
|
+
bool is_convertible(VALUE value)
|
552
|
+
{
|
553
|
+
return false;
|
554
|
+
}
|
555
|
+
|
556
|
+
std::monostate& convert(VALUE value)
|
557
|
+
{
|
558
|
+
return this->converted_;
|
559
|
+
}
|
560
|
+
|
561
|
+
private:
|
562
|
+
std::monostate converted_ = std::monostate();
|
563
|
+
};
|
609
564
|
}
|
610
565
|
|
566
|
+
// ========= variant.hpp =========
|
611
567
|
|
612
|
-
// --------- vector.ipp ---------
|
613
568
|
|
614
|
-
|
615
|
-
#include <
|
616
|
-
#include <vector>
|
569
|
+
// --------- variant.ipp ---------
|
570
|
+
#include <variant>
|
617
571
|
|
618
|
-
namespace Rice
|
572
|
+
namespace Rice::detail
|
619
573
|
{
|
620
|
-
|
574
|
+
template<typename...Types>
|
575
|
+
struct Type<std::variant<Types...>>
|
621
576
|
{
|
622
|
-
|
623
|
-
|
577
|
+
using Tuple_T = std::tuple<Types...>;
|
578
|
+
|
579
|
+
template<std::size_t... I>
|
580
|
+
constexpr static bool verifyTypes(std::index_sequence<I...>& indices)
|
624
581
|
{
|
625
|
-
|
626
|
-
|
627
|
-
using Difference_T = typename T::difference_type;
|
582
|
+
return (Type<std::tuple_element_t<I, Tuple_T>>::verify() && ...);
|
583
|
+
}
|
628
584
|
|
629
|
-
|
630
|
-
|
631
|
-
|
632
|
-
|
633
|
-
|
634
|
-
|
635
|
-
|
636
|
-
this->define_access_methods();
|
637
|
-
this->define_comparable_methods();
|
638
|
-
this->define_modify_methods();
|
639
|
-
this->define_to_s();
|
640
|
-
this->define_enumerable();
|
641
|
-
}
|
585
|
+
template<std::size_t... I>
|
586
|
+
constexpr static bool verify()
|
587
|
+
{
|
588
|
+
auto indices = std::make_index_sequence<std::variant_size_v<std::variant<Types...>>>{};
|
589
|
+
return verifyTypes(indices);
|
590
|
+
}
|
591
|
+
};
|
642
592
|
|
643
|
-
|
593
|
+
template<typename...Types>
|
594
|
+
class To_Ruby<std::variant<Types...>>
|
595
|
+
{
|
596
|
+
public:
|
644
597
|
|
645
|
-
|
646
|
-
|
647
|
-
|
648
|
-
|
649
|
-
|
650
|
-
if (index < 0)
|
651
|
-
{
|
652
|
-
index = ((-index) % size);
|
653
|
-
index = index > 0 ? size - index : index;
|
654
|
-
}
|
598
|
+
template<typename T>
|
599
|
+
static VALUE convertElement(std::variant<Types...>& data, bool takeOwnership)
|
600
|
+
{
|
601
|
+
return To_Ruby<T>().convert(std::get<T>(data));
|
602
|
+
}
|
655
603
|
|
656
|
-
|
604
|
+
template<std::size_t... I>
|
605
|
+
static VALUE convertIterator(std::variant<Types...>& data, bool takeOwnership, std::index_sequence<I...>& indices)
|
606
|
+
{
|
607
|
+
// Create a tuple of the variant types so we can look over the tuple's types
|
608
|
+
using Tuple_T = std::tuple<Types...>;
|
609
|
+
|
610
|
+
/* This is a fold expression. In pseudo code:
|
611
|
+
|
612
|
+
for (type in variant.types)
|
657
613
|
{
|
658
|
-
|
614
|
+
if (variant.has_value<type>())
|
615
|
+
return ToRuby<type>().convert(variant.getValue<type>)
|
616
|
+
}
|
617
|
+
|
618
|
+
The list of variant types is stored in Tuple_T. The number of types is stored in I.
|
619
|
+
Starting with index 0, get the variant type using td::tuple_element_t<I, Tuple_T>>.
|
620
|
+
Next check if the variant has a value for that type using std::holds_alternative<T>.
|
621
|
+
If yes, then call convertElement and save the return value to result. Then use the
|
622
|
+
comma operator to return true to the fold expression. If the variant does not have
|
623
|
+
a value for the type then return false.
|
624
|
+
|
625
|
+
The fold operator is or (||). If an index returns false, then the next index is evaulated
|
626
|
+
up until I.
|
627
|
+
|
628
|
+
Code inspired by https://www.foonathan.net/2020/05/fold-tricks/ */
|
629
|
+
|
630
|
+
VALUE result = Qnil;
|
631
|
+
((std::holds_alternative<std::tuple_element_t<I, Tuple_T>>(data) ?
|
632
|
+
(result = convertElement<std::tuple_element_t<I, Tuple_T>>(data, takeOwnership), true) : false) || ...);
|
633
|
+
|
634
|
+
return result;
|
635
|
+
}
|
636
|
+
|
637
|
+
static VALUE convert(std::variant<Types...>& data, bool takeOwnership = false)
|
638
|
+
{
|
639
|
+
auto indices = std::make_index_sequence<std::variant_size_v<std::variant<Types...>>>{};
|
640
|
+
return convertIterator(data, takeOwnership, indices);
|
641
|
+
}
|
642
|
+
};
|
643
|
+
|
644
|
+
template<typename...Types>
|
645
|
+
class To_Ruby<std::variant<Types...>&>
|
646
|
+
{
|
647
|
+
public:
|
648
|
+
template<typename T>
|
649
|
+
static VALUE convertElement(std::variant<Types...>& data, bool takeOwnership)
|
650
|
+
{
|
651
|
+
return To_Ruby<T>().convert(std::get<T>(data));
|
652
|
+
}
|
653
|
+
|
654
|
+
template<std::size_t... I>
|
655
|
+
static VALUE convertIterator(std::variant<Types...>& data, bool takeOwnership, std::index_sequence<I...>& indices)
|
656
|
+
{
|
657
|
+
// Create a tuple of the variant types so we can look over the tuple's types
|
658
|
+
using Tuple_T = std::tuple<Types...>;
|
659
|
+
|
660
|
+
// See comments above for explanation of this code
|
661
|
+
VALUE result = Qnil;
|
662
|
+
((std::holds_alternative<std::tuple_element_t<I, Tuple_T>>(data) ?
|
663
|
+
(result = convertElement<std::tuple_element_t<I, Tuple_T>>(data, takeOwnership), true) : false) || ...);
|
664
|
+
|
665
|
+
return result;
|
666
|
+
}
|
667
|
+
|
668
|
+
static VALUE convert(std::variant<Types...>& data, bool takeOwnership = false)
|
669
|
+
{
|
670
|
+
auto indices = std::make_index_sequence<std::variant_size_v<std::variant<Types...>>>{};
|
671
|
+
return convertIterator(data, takeOwnership, indices);
|
672
|
+
}
|
673
|
+
};
|
674
|
+
|
675
|
+
template<typename...Types>
|
676
|
+
class From_Ruby<std::variant<Types...>>
|
677
|
+
{
|
678
|
+
private:
|
679
|
+
// Possible converters we could use for this variant
|
680
|
+
using From_Ruby_Ts = std::tuple<From_Ruby<Types>...>;
|
681
|
+
|
682
|
+
public:
|
683
|
+
/* This method loops over each type in the variant, creates a From_Ruby converter,
|
684
|
+
and then check if the converter can work with the provided Rby value (it checks
|
685
|
+
the type of the Ruby object to see if it matches the variant type).
|
686
|
+
If yes, then the converter runs. If no, then the method recursively calls itself
|
687
|
+
increasing the index.
|
688
|
+
|
689
|
+
We use recursion, with a constexpr, to avoid having to instantiate an instance
|
690
|
+
of the variant to store results from a fold expression like the To_Ruby code
|
691
|
+
does above. That allows us to process variants with non default constructible
|
692
|
+
arguments like std::reference_wrapper. */
|
693
|
+
template <std::size_t I = 0>
|
694
|
+
std::variant<Types...> convertInternal(VALUE value)
|
695
|
+
{
|
696
|
+
// Loop over each possible type in the variant.
|
697
|
+
if constexpr (I < std::variant_size_v<std::variant<Types...>>)
|
698
|
+
{
|
699
|
+
// Get the converter for the current index
|
700
|
+
typename std::tuple_element_t<I, From_Ruby_Ts> converter;
|
701
|
+
|
702
|
+
// See if it will work
|
703
|
+
if (converter.is_convertible(value))
|
704
|
+
{
|
705
|
+
return converter.convert(value);
|
706
|
+
}
|
707
|
+
else
|
708
|
+
{
|
709
|
+
return convertInternal<I + 1>(value);
|
710
|
+
}
|
711
|
+
}
|
712
|
+
throw std::runtime_error("Could not find converter for variant");
|
713
|
+
}
|
714
|
+
|
715
|
+
std::variant<Types...> convert(VALUE value)
|
716
|
+
{
|
717
|
+
return convertInternal(value);
|
718
|
+
}
|
719
|
+
};
|
720
|
+
|
721
|
+
template<typename...Types>
|
722
|
+
class From_Ruby<std::variant<Types...>&>
|
723
|
+
{
|
724
|
+
private:
|
725
|
+
// Possible converters we could use for this variant
|
726
|
+
using From_Ruby_Ts = std::tuple<From_Ruby<Types>...>;
|
727
|
+
|
728
|
+
public:
|
729
|
+
template <std::size_t I = 0>
|
730
|
+
std::variant<Types...> convertInternal(VALUE value)
|
731
|
+
{
|
732
|
+
// Loop over each possible type in the variant
|
733
|
+
if constexpr (I < std::variant_size_v<std::variant<Types...>>)
|
734
|
+
{
|
735
|
+
// Get the converter for the current index
|
736
|
+
typename std::tuple_element_t<I, From_Ruby_Ts> converter;
|
737
|
+
|
738
|
+
// See if it will work
|
739
|
+
if (converter.is_convertible(value))
|
740
|
+
{
|
741
|
+
return converter.convert(value);
|
742
|
+
}
|
743
|
+
else
|
744
|
+
{
|
745
|
+
return convertInternal<I + 1>(value);
|
746
|
+
}
|
747
|
+
}
|
748
|
+
throw std::runtime_error("Could not find converter for variant");
|
749
|
+
}
|
750
|
+
|
751
|
+
std::variant<Types...> convert(VALUE value)
|
752
|
+
{
|
753
|
+
return convertInternal(value);
|
754
|
+
}
|
755
|
+
};
|
756
|
+
}
|
757
|
+
|
758
|
+
// ========= pair.hpp =========
|
759
|
+
|
760
|
+
|
761
|
+
namespace Rice
|
762
|
+
{
|
763
|
+
template<typename T>
|
764
|
+
Data_Type<T> define_pair(std::string name);
|
765
|
+
|
766
|
+
template<typename T>
|
767
|
+
Data_Type<T> define_pair_under(Object module, std::string name);
|
768
|
+
}
|
769
|
+
|
770
|
+
|
771
|
+
// --------- pair.ipp ---------
|
772
|
+
|
773
|
+
#include <sstream>
|
774
|
+
#include <stdexcept>
|
775
|
+
#include <utility>
|
776
|
+
|
777
|
+
namespace Rice
|
778
|
+
{
|
779
|
+
namespace stl
|
780
|
+
{
|
781
|
+
template<typename T>
|
782
|
+
class PairHelper
|
783
|
+
{
|
784
|
+
public:
|
785
|
+
PairHelper(Data_Type<T> klass) : klass_(klass)
|
786
|
+
{
|
787
|
+
this->define_constructor();
|
788
|
+
this->define_copyable_methods();
|
789
|
+
this->define_access_methods();
|
790
|
+
this->define_modify_methods();
|
791
|
+
this->define_to_s();
|
792
|
+
}
|
793
|
+
|
794
|
+
private:
|
795
|
+
void define_constructor()
|
796
|
+
{
|
797
|
+
klass_.define_constructor(Constructor<T, typename T::first_type&, typename T::second_type&>());
|
798
|
+
}
|
799
|
+
|
800
|
+
void define_copyable_methods()
|
801
|
+
{
|
802
|
+
if constexpr (std::is_copy_constructible_v<typename T::first_type> && std::is_copy_constructible_v<typename T::second_type>)
|
803
|
+
{
|
804
|
+
klass_.define_method("copy", [](T& pair) -> T
|
805
|
+
{
|
806
|
+
return pair;
|
807
|
+
});
|
808
|
+
}
|
809
|
+
else
|
810
|
+
{
|
811
|
+
klass_.define_method("copy", [](T& pair) -> T
|
812
|
+
{
|
813
|
+
throw std::runtime_error("Cannot copy pair with non-copy constructible types");
|
814
|
+
return pair;
|
815
|
+
});
|
816
|
+
}
|
817
|
+
}
|
818
|
+
|
819
|
+
void define_access_methods()
|
820
|
+
{
|
821
|
+
// Access methods
|
822
|
+
klass_.define_method("first", [](T& pair) -> typename T::first_type&
|
823
|
+
{
|
824
|
+
return pair.first;
|
825
|
+
})
|
826
|
+
.define_method("second", [](T& pair) -> typename T::second_type&
|
827
|
+
{
|
828
|
+
return pair.second;
|
829
|
+
});
|
830
|
+
}
|
831
|
+
|
832
|
+
void define_modify_methods()
|
833
|
+
{
|
834
|
+
// Access methods
|
835
|
+
klass_.define_method("first=", [](T& pair, typename T::first_type& value) -> typename T::first_type&
|
836
|
+
{
|
837
|
+
if constexpr (std::is_const_v<std::remove_reference_t<std::remove_pointer_t<typename T::first_type>>>)
|
838
|
+
{
|
839
|
+
throw std::runtime_error("Cannot set pair.first since it is a constant");
|
840
|
+
}
|
841
|
+
else
|
842
|
+
{
|
843
|
+
pair.first = value;
|
844
|
+
return pair.first;
|
845
|
+
}
|
846
|
+
})
|
847
|
+
.define_method("second=", [](T& pair, typename T::second_type& value) -> typename T::second_type&
|
848
|
+
{
|
849
|
+
if constexpr (std::is_const_v<std::remove_reference_t<std::remove_pointer_t<typename T::second_type>>>)
|
850
|
+
{
|
851
|
+
throw std::runtime_error("Cannot set pair.second since it is a constant");
|
852
|
+
}
|
853
|
+
else
|
854
|
+
{
|
855
|
+
pair.second = value;
|
856
|
+
return pair.second;
|
857
|
+
}
|
858
|
+
});
|
859
|
+
}
|
860
|
+
|
861
|
+
void define_to_s()
|
862
|
+
{
|
863
|
+
if constexpr (detail::is_ostreamable_v<typename T::first_type> && detail::is_ostreamable_v<typename T::second_type>)
|
864
|
+
{
|
865
|
+
klass_.define_method("to_s", [](const T& pair)
|
866
|
+
{
|
867
|
+
std::stringstream stream;
|
868
|
+
stream << "[" << pair.first << ", " << pair.second << "]";
|
869
|
+
return stream.str();
|
870
|
+
});
|
871
|
+
}
|
872
|
+
else
|
873
|
+
{
|
874
|
+
klass_.define_method("to_s", [](const T& pair)
|
875
|
+
{
|
876
|
+
return "[Not printable]";
|
877
|
+
});
|
878
|
+
}
|
879
|
+
}
|
880
|
+
|
881
|
+
private:
|
882
|
+
Data_Type<T> klass_;
|
883
|
+
};
|
884
|
+
} // namespace
|
885
|
+
|
886
|
+
template<typename T>
|
887
|
+
Data_Type<T> define_pair_under(Object module, std::string name)
|
888
|
+
{
|
889
|
+
if (detail::Registries::instance.types.isDefined<T>())
|
890
|
+
{
|
891
|
+
// If the pair has been previously seen it will be registered but may
|
892
|
+
// not be associated with the constant Module::<name>
|
893
|
+
module.const_set_maybe(name, Data_Type<T>().klass());
|
894
|
+
return Data_Type<T>();
|
895
|
+
}
|
896
|
+
|
897
|
+
Data_Type<T> result = define_class_under<detail::intrinsic_type<T>>(module, name.c_str());
|
898
|
+
stl::PairHelper helper(result);
|
899
|
+
return result;
|
900
|
+
}
|
901
|
+
|
902
|
+
template<typename T>
|
903
|
+
Data_Type<T> define_pair(std::string name)
|
904
|
+
{
|
905
|
+
if (detail::Registries::instance.types.isDefined<T>())
|
906
|
+
{
|
907
|
+
// If the pair has been previously seen it will be registered but may
|
908
|
+
// not be associated with the constant Object::<name>
|
909
|
+
Object(rb_cObject).const_set_maybe(name, Data_Type<T>().klass());
|
910
|
+
return Data_Type<T>();
|
911
|
+
}
|
912
|
+
|
913
|
+
Data_Type<T> result = define_class<detail::intrinsic_type<T>>(name.c_str());
|
914
|
+
stl::PairHelper<T> helper(result);
|
915
|
+
return result;
|
916
|
+
}
|
917
|
+
|
918
|
+
template<typename T>
|
919
|
+
Data_Type<T> define_pair_auto()
|
920
|
+
{
|
921
|
+
std::string klassName = detail::makeClassName(typeid(T));
|
922
|
+
Module rb_mRice = define_module("Rice");
|
923
|
+
Module rb_mpair = define_module_under(rb_mRice, "Std");
|
924
|
+
return define_pair_under<T>(rb_mpair, klassName);
|
925
|
+
}
|
926
|
+
|
927
|
+
namespace detail
|
928
|
+
{
|
929
|
+
template<typename T1, typename T2>
|
930
|
+
struct Type<std::pair<T1, T2>>
|
931
|
+
{
|
932
|
+
static bool verify()
|
933
|
+
{
|
934
|
+
detail::verifyType<T1>();
|
935
|
+
detail::verifyType<T2>();
|
936
|
+
|
937
|
+
if (!detail::Registries::instance.types.isDefined<std::pair<T1, T2>>())
|
938
|
+
{
|
939
|
+
define_pair_auto<std::pair<T1, T2>>();
|
940
|
+
}
|
941
|
+
|
942
|
+
return true;
|
943
|
+
}
|
944
|
+
};
|
945
|
+
}
|
946
|
+
}
|
947
|
+
|
948
|
+
|
949
|
+
|
950
|
+
// ========= map.hpp =========
|
951
|
+
|
952
|
+
|
953
|
+
namespace Rice
|
954
|
+
{
|
955
|
+
template<typename U>
|
956
|
+
Data_Type<U> define_map(std::string name);
|
957
|
+
|
958
|
+
template<typename U>
|
959
|
+
Data_Type<U> define_map_under(Object module, std::string name);
|
960
|
+
}
|
961
|
+
|
962
|
+
|
963
|
+
// --------- map.ipp ---------
|
964
|
+
|
965
|
+
#include <sstream>
|
966
|
+
#include <stdexcept>
|
967
|
+
#include <map>
|
968
|
+
#include <type_traits>
|
969
|
+
#include <variant>
|
970
|
+
|
971
|
+
namespace Rice
|
972
|
+
{
|
973
|
+
namespace stl
|
974
|
+
{
|
975
|
+
template<typename T>
|
976
|
+
class MapHelper
|
977
|
+
{
|
978
|
+
using Key_T = typename T::key_type;
|
979
|
+
using Mapped_T = typename T::mapped_type;
|
980
|
+
using Value_T = typename T::value_type;
|
981
|
+
using Size_T = typename T::size_type;
|
982
|
+
using Difference_T = typename T::difference_type;
|
983
|
+
|
984
|
+
public:
|
985
|
+
MapHelper(Data_Type<T> klass) : klass_(klass)
|
986
|
+
{
|
987
|
+
this->register_pair();
|
988
|
+
this->define_constructor();
|
989
|
+
this->define_copyable_methods();
|
990
|
+
this->define_capacity_methods();
|
991
|
+
this->define_access_methods();
|
992
|
+
this->define_comparable_methods();
|
993
|
+
this->define_modify_methods();
|
994
|
+
this->define_enumerable();
|
995
|
+
this->define_to_s();
|
996
|
+
this->define_to_hash();
|
997
|
+
}
|
998
|
+
|
999
|
+
private:
|
1000
|
+
|
1001
|
+
void register_pair()
|
1002
|
+
{
|
1003
|
+
define_pair_auto<Value_T>();
|
1004
|
+
}
|
1005
|
+
|
1006
|
+
void define_constructor()
|
1007
|
+
{
|
1008
|
+
klass_.define_constructor(Constructor<T>());
|
1009
|
+
}
|
1010
|
+
|
1011
|
+
void define_copyable_methods()
|
1012
|
+
{
|
1013
|
+
if constexpr (std::is_copy_constructible_v<Value_T>)
|
1014
|
+
{
|
1015
|
+
klass_.define_method("copy", [](T& map) -> T
|
1016
|
+
{
|
1017
|
+
return map;
|
1018
|
+
});
|
1019
|
+
}
|
1020
|
+
else
|
1021
|
+
{
|
1022
|
+
klass_.define_method("copy", [](T& map) -> T
|
1023
|
+
{
|
1024
|
+
throw std::runtime_error("Cannot copy maps with non-copy constructible types");
|
1025
|
+
return map;
|
1026
|
+
});
|
1027
|
+
}
|
1028
|
+
}
|
1029
|
+
|
1030
|
+
void define_capacity_methods()
|
1031
|
+
{
|
1032
|
+
klass_.define_method("empty?", &T::empty)
|
1033
|
+
.define_method("max_size", &T::max_size)
|
1034
|
+
.define_method("size", &T::size);
|
1035
|
+
|
1036
|
+
rb_define_alias(klass_, "count", "size");
|
1037
|
+
rb_define_alias(klass_, "length", "size");
|
1038
|
+
}
|
1039
|
+
|
1040
|
+
void define_access_methods()
|
1041
|
+
{
|
1042
|
+
// Access methods
|
1043
|
+
klass_.define_method("[]", [](const T& map, const Key_T& key) -> std::optional<Mapped_T>
|
1044
|
+
{
|
1045
|
+
auto iter = map.find(key);
|
1046
|
+
|
1047
|
+
if (iter != map.end())
|
1048
|
+
{
|
1049
|
+
return iter->second;
|
1050
|
+
}
|
1051
|
+
else
|
1052
|
+
{
|
1053
|
+
return std::nullopt;
|
1054
|
+
}
|
1055
|
+
})
|
1056
|
+
.define_method("include?", [](T& map, Key_T& key) -> bool
|
1057
|
+
{
|
1058
|
+
return map.find(key) != map.end();
|
1059
|
+
})
|
1060
|
+
.define_method("keys", [](T& map) -> std::vector<Key_T>
|
1061
|
+
{
|
1062
|
+
std::vector<Key_T> result;
|
1063
|
+
std::transform(map.begin(), map.end(), std::back_inserter(result),
|
1064
|
+
[](const auto& pair)
|
1065
|
+
{
|
1066
|
+
return pair.first;
|
1067
|
+
});
|
1068
|
+
|
1069
|
+
return result;
|
1070
|
+
})
|
1071
|
+
.define_method("values", [](T& map) -> std::vector<Mapped_T>
|
1072
|
+
{
|
1073
|
+
std::vector<Mapped_T> result;
|
1074
|
+
std::transform(map.begin(), map.end(), std::back_inserter(result),
|
1075
|
+
[](const auto& pair)
|
1076
|
+
{
|
1077
|
+
return pair.second;
|
1078
|
+
});
|
1079
|
+
|
1080
|
+
return result;
|
1081
|
+
});
|
1082
|
+
|
1083
|
+
rb_define_alias(klass_, "has_key", "include?");
|
1084
|
+
}
|
1085
|
+
|
1086
|
+
// Methods that require Value_T to support operator==
|
1087
|
+
void define_comparable_methods()
|
1088
|
+
{
|
1089
|
+
if constexpr (detail::is_comparable_v<Mapped_T>)
|
1090
|
+
{
|
1091
|
+
klass_.define_method("value?", [](T& map, Mapped_T& value) -> bool
|
1092
|
+
{
|
1093
|
+
auto it = std::find_if(map.begin(), map.end(),
|
1094
|
+
[&value](auto& pair)
|
1095
|
+
{
|
1096
|
+
return pair.second == value;
|
1097
|
+
});
|
1098
|
+
|
1099
|
+
return it != map.end();
|
1100
|
+
});
|
1101
|
+
}
|
1102
|
+
else
|
1103
|
+
{
|
1104
|
+
klass_.define_method("value?", [](T& map, Mapped_T& value) -> bool
|
1105
|
+
{
|
1106
|
+
return false;
|
1107
|
+
});
|
1108
|
+
}
|
1109
|
+
|
1110
|
+
rb_define_alias(klass_, "has_value", "value?");
|
1111
|
+
}
|
1112
|
+
|
1113
|
+
void define_modify_methods()
|
1114
|
+
{
|
1115
|
+
klass_.define_method("clear", &T::clear)
|
1116
|
+
.define_method("delete", [](T& map, Key_T& key) -> std::optional<Mapped_T>
|
1117
|
+
{
|
1118
|
+
auto iter = map.find(key);
|
1119
|
+
|
1120
|
+
if (iter != map.end())
|
1121
|
+
{
|
1122
|
+
Mapped_T result = iter->second;
|
1123
|
+
map.erase(iter);
|
1124
|
+
return result;
|
1125
|
+
}
|
1126
|
+
else
|
1127
|
+
{
|
1128
|
+
return std::nullopt;
|
1129
|
+
}
|
1130
|
+
})
|
1131
|
+
.define_method("[]=", [](T& map, Key_T key, Mapped_T value) -> Mapped_T
|
1132
|
+
{
|
1133
|
+
map[key] = value;
|
1134
|
+
return value;
|
1135
|
+
});
|
1136
|
+
|
1137
|
+
rb_define_alias(klass_, "store", "[]=");
|
1138
|
+
}
|
1139
|
+
|
1140
|
+
void define_enumerable()
|
1141
|
+
{
|
1142
|
+
// Add enumerable support
|
1143
|
+
klass_.template define_iterator<typename T::iterator (T::*)()>(&T::begin, &T::end);
|
1144
|
+
}
|
1145
|
+
|
1146
|
+
void define_to_hash()
|
1147
|
+
{
|
1148
|
+
// Add enumerable support
|
1149
|
+
klass_.define_method("to_h", [](T& map)
|
1150
|
+
{
|
1151
|
+
VALUE result = rb_hash_new();
|
1152
|
+
std::for_each(map.begin(), map.end(), [&result](const typename T::reference pair)
|
1153
|
+
{
|
1154
|
+
VALUE key = detail::To_Ruby<Key_T&>().convert(pair.first);
|
1155
|
+
VALUE value = detail::To_Ruby<Mapped_T&>().convert(pair.second);
|
1156
|
+
rb_hash_aset(result, key, value);
|
1157
|
+
});
|
1158
|
+
|
1159
|
+
return result;
|
1160
|
+
}, Return().setValue());
|
1161
|
+
}
|
1162
|
+
|
1163
|
+
void define_to_s()
|
1164
|
+
{
|
1165
|
+
if constexpr (detail::is_ostreamable_v<Key_T> && detail::is_ostreamable_v<Mapped_T>)
|
1166
|
+
{
|
1167
|
+
klass_.define_method("to_s", [](const T& map)
|
1168
|
+
{
|
1169
|
+
auto iter = map.begin();
|
1170
|
+
|
1171
|
+
std::stringstream stream;
|
1172
|
+
stream << "{";
|
1173
|
+
|
1174
|
+
for (; iter != map.end(); iter++)
|
1175
|
+
{
|
1176
|
+
if (iter != map.begin())
|
1177
|
+
{
|
1178
|
+
stream << ", ";
|
1179
|
+
}
|
1180
|
+
stream << iter->first << " => " << iter->second;
|
1181
|
+
}
|
1182
|
+
|
1183
|
+
stream << "}";
|
1184
|
+
return stream.str();
|
1185
|
+
});
|
1186
|
+
}
|
1187
|
+
else
|
1188
|
+
{
|
1189
|
+
klass_.define_method("to_s", [](const T& map)
|
1190
|
+
{
|
1191
|
+
return "[Not printable]";
|
1192
|
+
});
|
1193
|
+
}
|
1194
|
+
}
|
1195
|
+
|
1196
|
+
private:
|
1197
|
+
Data_Type<T> klass_;
|
1198
|
+
};
|
1199
|
+
} // namespace
|
1200
|
+
|
1201
|
+
template<typename T>
|
1202
|
+
Data_Type<T> define_map_under(Object module, std::string name)
|
1203
|
+
{
|
1204
|
+
if (detail::Registries::instance.types.isDefined<T>())
|
1205
|
+
{
|
1206
|
+
// If the map has been previously seen it will be registered but may
|
1207
|
+
// not be associated with the constant Module::<name>
|
1208
|
+
module.const_set_maybe(name, Data_Type<T>().klass());
|
1209
|
+
return Data_Type<T>();
|
1210
|
+
}
|
1211
|
+
|
1212
|
+
Data_Type<T> result = define_class_under<detail::intrinsic_type<T>>(module, name.c_str());
|
1213
|
+
stl::MapHelper helper(result);
|
1214
|
+
return result;
|
1215
|
+
}
|
1216
|
+
|
1217
|
+
template<typename T>
|
1218
|
+
Data_Type<T> define_map(std::string name)
|
1219
|
+
{
|
1220
|
+
if (detail::Registries::instance.types.isDefined<T>())
|
1221
|
+
{
|
1222
|
+
// If the map has been previously seen it will be registered but may
|
1223
|
+
// not be associated with the constant Object::<name>
|
1224
|
+
Object(rb_cObject).const_set_maybe(name, Data_Type<T>().klass());
|
1225
|
+
return Data_Type<T>();
|
1226
|
+
}
|
1227
|
+
|
1228
|
+
Data_Type<T> result = define_class<detail::intrinsic_type<T>>(name.c_str());
|
1229
|
+
stl::MapHelper<T> helper(result);
|
1230
|
+
return result;
|
1231
|
+
}
|
1232
|
+
|
1233
|
+
template<typename T>
|
1234
|
+
Data_Type<T> define_map_auto()
|
1235
|
+
{
|
1236
|
+
std::string klassName = detail::makeClassName(typeid(T));
|
1237
|
+
Module rb_mRice = define_module("Rice");
|
1238
|
+
Module rb_mmap = define_module_under(rb_mRice, "Std");
|
1239
|
+
return define_map_under<T>(rb_mmap, klassName);
|
1240
|
+
}
|
1241
|
+
|
1242
|
+
namespace detail
|
1243
|
+
{
|
1244
|
+
template<typename T, typename U>
|
1245
|
+
struct Type<std::map<T, U>>
|
1246
|
+
{
|
1247
|
+
static bool verify()
|
1248
|
+
{
|
1249
|
+
Type<T>::verify();
|
1250
|
+
Type<U>::verify();
|
1251
|
+
|
1252
|
+
if (!detail::Registries::instance.types.isDefined<std::map<T, U>>())
|
1253
|
+
{
|
1254
|
+
define_map_auto<std::map<T, U>>();
|
1255
|
+
}
|
1256
|
+
|
1257
|
+
return true;
|
1258
|
+
}
|
1259
|
+
};
|
1260
|
+
|
1261
|
+
template<typename T, typename U>
|
1262
|
+
struct MapFromHash
|
1263
|
+
{
|
1264
|
+
static int convertPair(VALUE key, VALUE value, VALUE user_data)
|
1265
|
+
{
|
1266
|
+
std::map<T, U>* result = (std::map<T, U>*)(user_data);
|
1267
|
+
|
1268
|
+
// This method is being called from Ruby so we cannot let any C++
|
1269
|
+
// exceptions propogate back to Ruby
|
1270
|
+
return cpp_protect([&]
|
1271
|
+
{
|
1272
|
+
result->operator[](From_Ruby<T>().convert(key)) = From_Ruby<U>().convert(value);
|
1273
|
+
return ST_CONTINUE;
|
1274
|
+
});
|
1275
|
+
}
|
1276
|
+
|
1277
|
+
static std::map<T, U> convert(VALUE value)
|
1278
|
+
{
|
1279
|
+
std::map<T, U> result;
|
1280
|
+
VALUE user_data = (VALUE)(&result);
|
1281
|
+
|
1282
|
+
// MSVC needs help here, but g++ does not
|
1283
|
+
using Rb_Hash_ForEach_T = void(*)(VALUE, int(*)(VALUE, VALUE, VALUE), VALUE);
|
1284
|
+
detail::protect<Rb_Hash_ForEach_T>(rb_hash_foreach, value, convertPair, user_data);
|
1285
|
+
|
1286
|
+
return result;
|
1287
|
+
}
|
1288
|
+
};
|
1289
|
+
|
1290
|
+
template<typename T, typename U>
|
1291
|
+
class From_Ruby<std::map<T, U>>
|
1292
|
+
{
|
1293
|
+
public:
|
1294
|
+
From_Ruby() = default;
|
1295
|
+
|
1296
|
+
explicit From_Ruby(Arg * arg) : arg_(arg)
|
1297
|
+
{
|
1298
|
+
}
|
1299
|
+
|
1300
|
+
std::map<T, U> convert(VALUE value)
|
1301
|
+
{
|
1302
|
+
switch (rb_type(value))
|
1303
|
+
{
|
1304
|
+
case T_DATA:
|
1305
|
+
{
|
1306
|
+
// This is a wrapped map (hopefully!)
|
1307
|
+
return *Data_Object<std::map<T, U>>::from_ruby(value);
|
1308
|
+
}
|
1309
|
+
case T_HASH:
|
1310
|
+
{
|
1311
|
+
// If this an Ruby hash and the mapped type is copyable
|
1312
|
+
if constexpr (std::is_default_constructible_v<U>)
|
1313
|
+
{
|
1314
|
+
return MapFromHash<T, U>::convert(value);
|
1315
|
+
}
|
1316
|
+
}
|
1317
|
+
case T_NIL:
|
1318
|
+
{
|
1319
|
+
if (this->arg_ && this->arg_->hasDefaultValue())
|
1320
|
+
{
|
1321
|
+
return this->arg_->template defaultValue<std::map<T, U>>();
|
1322
|
+
}
|
1323
|
+
}
|
1324
|
+
default:
|
1325
|
+
{
|
1326
|
+
throw Exception(rb_eTypeError, "wrong argument type %s (expected % s)",
|
1327
|
+
detail::protect(rb_obj_classname, value), "std::map");
|
1328
|
+
}
|
1329
|
+
}
|
1330
|
+
}
|
1331
|
+
|
1332
|
+
private:
|
1333
|
+
Arg* arg_ = nullptr;
|
1334
|
+
};
|
1335
|
+
|
1336
|
+
template<typename T, typename U>
|
1337
|
+
class From_Ruby<std::map<T, U>&>
|
1338
|
+
{
|
1339
|
+
public:
|
1340
|
+
From_Ruby() = default;
|
1341
|
+
|
1342
|
+
explicit From_Ruby(Arg * arg) : arg_(arg)
|
1343
|
+
{
|
1344
|
+
}
|
1345
|
+
|
1346
|
+
std::map<T, U>& convert(VALUE value)
|
1347
|
+
{
|
1348
|
+
switch (rb_type(value))
|
1349
|
+
{
|
1350
|
+
case T_DATA:
|
1351
|
+
{
|
1352
|
+
// This is a wrapped map (hopefully!)
|
1353
|
+
return *Data_Object<std::map<T, U>>::from_ruby(value);
|
1354
|
+
}
|
1355
|
+
case T_HASH:
|
1356
|
+
{
|
1357
|
+
// If this an Ruby array and the map type is copyable
|
1358
|
+
if constexpr (std::is_default_constructible_v<std::map<T, U>>)
|
1359
|
+
{
|
1360
|
+
this->converted_ = MapFromHash<T, U>::convert(value);
|
1361
|
+
return this->converted_;
|
1362
|
+
}
|
1363
|
+
}
|
1364
|
+
case T_NIL:
|
1365
|
+
{
|
1366
|
+
if (this->arg_ && this->arg_->hasDefaultValue())
|
1367
|
+
{
|
1368
|
+
return this->arg_->template defaultValue<std::map<T, U>>();
|
1369
|
+
}
|
1370
|
+
}
|
1371
|
+
default:
|
1372
|
+
{
|
1373
|
+
throw Exception(rb_eTypeError, "wrong argument type %s (expected % s)",
|
1374
|
+
detail::protect(rb_obj_classname, value), "std::map");
|
1375
|
+
}
|
1376
|
+
}
|
1377
|
+
}
|
1378
|
+
|
1379
|
+
private:
|
1380
|
+
Arg* arg_ = nullptr;
|
1381
|
+
std::map<T, U> converted_;
|
1382
|
+
};
|
1383
|
+
|
1384
|
+
template<typename T, typename U>
|
1385
|
+
class From_Ruby<std::map<T, U>*>
|
1386
|
+
{
|
1387
|
+
public:
|
1388
|
+
std::map<T, U>* convert(VALUE value)
|
1389
|
+
{
|
1390
|
+
switch (rb_type(value))
|
1391
|
+
{
|
1392
|
+
case T_DATA:
|
1393
|
+
{
|
1394
|
+
// This is a wrapped map (hopefully!)
|
1395
|
+
return Data_Object<std::map<T, U>>::from_ruby(value);
|
1396
|
+
}
|
1397
|
+
case T_HASH:
|
1398
|
+
{
|
1399
|
+
// If this an Ruby array and the map type is copyable
|
1400
|
+
if constexpr (std::is_default_constructible_v<U>)
|
1401
|
+
{
|
1402
|
+
this->converted_ = MapFromHash<T, U>::convert(value);
|
1403
|
+
return &this->converted_;
|
1404
|
+
}
|
1405
|
+
}
|
1406
|
+
default:
|
1407
|
+
{
|
1408
|
+
throw Exception(rb_eTypeError, "wrong argument type %s (expected % s)",
|
1409
|
+
detail::protect(rb_obj_classname, value), "std::map");
|
1410
|
+
}
|
1411
|
+
}
|
1412
|
+
}
|
1413
|
+
|
1414
|
+
private:
|
1415
|
+
std::map<T, U> converted_;
|
1416
|
+
};
|
1417
|
+
}
|
1418
|
+
}
|
1419
|
+
|
1420
|
+
// ========= unordered_map.hpp =========
|
1421
|
+
|
1422
|
+
|
1423
|
+
namespace Rice
|
1424
|
+
{
|
1425
|
+
template<typename U>
|
1426
|
+
Data_Type<U> define_unordered_map(std::string name);
|
1427
|
+
|
1428
|
+
template<typename U>
|
1429
|
+
Data_Type<U> define_unordered_map_under(Object module, std::string name);
|
1430
|
+
}
|
1431
|
+
|
1432
|
+
|
1433
|
+
// --------- unordered_map.ipp ---------
|
1434
|
+
|
1435
|
+
#include <sstream>
|
1436
|
+
#include <stdexcept>
|
1437
|
+
#include <type_traits>
|
1438
|
+
#include <unordered_map>
|
1439
|
+
#include <variant>
|
1440
|
+
|
1441
|
+
namespace Rice
|
1442
|
+
{
|
1443
|
+
namespace stl
|
1444
|
+
{
|
1445
|
+
template<typename T>
|
1446
|
+
class UnorderedMapHelper
|
1447
|
+
{
|
1448
|
+
using Key_T = typename T::key_type;
|
1449
|
+
using Mapped_T = typename T::mapped_type;
|
1450
|
+
using Value_T = typename T::value_type;
|
1451
|
+
using Size_T = typename T::size_type;
|
1452
|
+
using Difference_T = typename T::difference_type;
|
1453
|
+
|
1454
|
+
public:
|
1455
|
+
UnorderedMapHelper(Data_Type<T> klass) : klass_(klass)
|
1456
|
+
{
|
1457
|
+
this->register_pair();
|
1458
|
+
this->define_constructor();
|
1459
|
+
this->define_copyable_methods();
|
1460
|
+
this->define_capacity_methods();
|
1461
|
+
this->define_access_methods();
|
1462
|
+
this->define_comparable_methods();
|
1463
|
+
this->define_modify_methods();
|
1464
|
+
this->define_enumerable();
|
1465
|
+
this->define_to_s();
|
1466
|
+
this->define_to_hash();
|
1467
|
+
}
|
1468
|
+
|
1469
|
+
private:
|
1470
|
+
|
1471
|
+
void register_pair()
|
1472
|
+
{
|
1473
|
+
define_pair_auto<Value_T>();
|
1474
|
+
}
|
1475
|
+
|
1476
|
+
void define_constructor()
|
1477
|
+
{
|
1478
|
+
klass_.define_constructor(Constructor<T>());
|
1479
|
+
}
|
1480
|
+
|
1481
|
+
void define_copyable_methods()
|
1482
|
+
{
|
1483
|
+
if constexpr (std::is_copy_constructible_v<Value_T>)
|
1484
|
+
{
|
1485
|
+
klass_.define_method("copy", [](T& unordered_map) -> T
|
1486
|
+
{
|
1487
|
+
return unordered_map;
|
1488
|
+
});
|
1489
|
+
}
|
1490
|
+
else
|
1491
|
+
{
|
1492
|
+
klass_.define_method("copy", [](T& unordered_map) -> T
|
1493
|
+
{
|
1494
|
+
throw std::runtime_error("Cannot copy unordered_maps with non-copy constructible types");
|
1495
|
+
return unordered_map;
|
1496
|
+
});
|
1497
|
+
}
|
1498
|
+
}
|
1499
|
+
|
1500
|
+
void define_capacity_methods()
|
1501
|
+
{
|
1502
|
+
klass_.define_method("empty?", &T::empty)
|
1503
|
+
.define_method("max_size", &T::max_size)
|
1504
|
+
.define_method("size", &T::size);
|
1505
|
+
|
1506
|
+
rb_define_alias(klass_, "count", "size");
|
1507
|
+
rb_define_alias(klass_, "length", "size");
|
1508
|
+
}
|
1509
|
+
|
1510
|
+
void define_access_methods()
|
1511
|
+
{
|
1512
|
+
// Access methods
|
1513
|
+
klass_.define_method("[]", [](const T& unordered_map, const Key_T& key) -> std::optional<Mapped_T>
|
1514
|
+
{
|
1515
|
+
auto iter = unordered_map.find(key);
|
1516
|
+
|
1517
|
+
if (iter != unordered_map.end())
|
1518
|
+
{
|
1519
|
+
return iter->second;
|
1520
|
+
}
|
1521
|
+
else
|
1522
|
+
{
|
1523
|
+
return std::nullopt;
|
1524
|
+
}
|
1525
|
+
})
|
1526
|
+
.define_method("include?", [](T& unordered_map, Key_T& key) -> bool
|
1527
|
+
{
|
1528
|
+
return unordered_map.find(key) != unordered_map.end();
|
1529
|
+
})
|
1530
|
+
.define_method("keys", [](T& unordered_map) -> std::vector<Key_T>
|
1531
|
+
{
|
1532
|
+
std::vector<Key_T> result;
|
1533
|
+
std::transform(unordered_map.begin(), unordered_map.end(), std::back_inserter(result),
|
1534
|
+
[](const auto& pair)
|
1535
|
+
{
|
1536
|
+
return pair.first;
|
1537
|
+
});
|
1538
|
+
|
1539
|
+
return result;
|
1540
|
+
})
|
1541
|
+
.define_method("values", [](T& unordered_map) -> std::vector<Mapped_T>
|
1542
|
+
{
|
1543
|
+
std::vector<Mapped_T> result;
|
1544
|
+
std::transform(unordered_map.begin(), unordered_map.end(), std::back_inserter(result),
|
1545
|
+
[](const auto& pair)
|
1546
|
+
{
|
1547
|
+
return pair.second;
|
1548
|
+
});
|
1549
|
+
|
1550
|
+
return result;
|
1551
|
+
});
|
1552
|
+
|
1553
|
+
rb_define_alias(klass_, "has_key", "include?");
|
1554
|
+
}
|
1555
|
+
|
1556
|
+
// Methods that require Value_T to support operator==
|
1557
|
+
void define_comparable_methods()
|
1558
|
+
{
|
1559
|
+
if constexpr (detail::is_comparable_v<Mapped_T>)
|
1560
|
+
{
|
1561
|
+
klass_.define_method("value?", [](T& unordered_map, Mapped_T& value) -> bool
|
1562
|
+
{
|
1563
|
+
auto it = std::find_if(unordered_map.begin(), unordered_map.end(),
|
1564
|
+
[&value](auto& pair)
|
1565
|
+
{
|
1566
|
+
return pair.second == value;
|
1567
|
+
});
|
1568
|
+
|
1569
|
+
return it != unordered_map.end();
|
1570
|
+
});
|
1571
|
+
}
|
1572
|
+
else
|
1573
|
+
{
|
1574
|
+
klass_.define_method("value?", [](T& unordered_map, Mapped_T& value) -> bool
|
1575
|
+
{
|
1576
|
+
return false;
|
1577
|
+
});
|
1578
|
+
}
|
1579
|
+
|
1580
|
+
rb_define_alias(klass_, "has_value", "value?");
|
1581
|
+
}
|
1582
|
+
|
1583
|
+
void define_modify_methods()
|
1584
|
+
{
|
1585
|
+
klass_.define_method("clear", &T::clear)
|
1586
|
+
.define_method("delete", [](T& unordered_map, Key_T& key) -> std::optional<Mapped_T>
|
1587
|
+
{
|
1588
|
+
auto iter = unordered_map.find(key);
|
1589
|
+
|
1590
|
+
if (iter != unordered_map.end())
|
1591
|
+
{
|
1592
|
+
Mapped_T result = iter->second;
|
1593
|
+
unordered_map.erase(iter);
|
1594
|
+
return result;
|
1595
|
+
}
|
1596
|
+
else
|
1597
|
+
{
|
1598
|
+
return std::nullopt;
|
1599
|
+
}
|
1600
|
+
})
|
1601
|
+
.define_method("[]=", [](T& unordered_map, Key_T key, Mapped_T value) -> Mapped_T
|
1602
|
+
{
|
1603
|
+
unordered_map[key] = value;
|
1604
|
+
return value;
|
1605
|
+
});
|
1606
|
+
|
1607
|
+
rb_define_alias(klass_, "store", "[]=");
|
1608
|
+
}
|
1609
|
+
|
1610
|
+
void define_enumerable()
|
1611
|
+
{
|
1612
|
+
// Add enumerable support
|
1613
|
+
klass_.template define_iterator<typename T::iterator (T::*)()>(&T::begin, &T::end);
|
1614
|
+
}
|
1615
|
+
|
1616
|
+
void define_to_hash()
|
1617
|
+
{
|
1618
|
+
// Add enumerable support
|
1619
|
+
klass_.define_method("to_h", [](T& unordered_map)
|
1620
|
+
{
|
1621
|
+
VALUE result = rb_hash_new();
|
1622
|
+
std::for_each(unordered_map.begin(), unordered_map.end(), [&result](const auto& pair)
|
1623
|
+
{
|
1624
|
+
VALUE key = detail::To_Ruby<Key_T>().convert(pair.first);
|
1625
|
+
VALUE value = detail::To_Ruby<Mapped_T>().convert(pair.second);
|
1626
|
+
rb_hash_aset(result, key, value);
|
1627
|
+
});
|
1628
|
+
|
1629
|
+
return result;
|
1630
|
+
}, Return().setValue());
|
1631
|
+
}
|
1632
|
+
|
1633
|
+
void define_to_s()
|
1634
|
+
{
|
1635
|
+
if constexpr (detail::is_ostreamable_v<Key_T> && detail::is_ostreamable_v<Mapped_T>)
|
1636
|
+
{
|
1637
|
+
klass_.define_method("to_s", [](const T& unordered_map)
|
1638
|
+
{
|
1639
|
+
auto iter = unordered_map.begin();
|
1640
|
+
|
1641
|
+
std::stringstream stream;
|
1642
|
+
stream << "{";
|
1643
|
+
|
1644
|
+
for (; iter != unordered_map.end(); iter++)
|
1645
|
+
{
|
1646
|
+
if (iter != unordered_map.begin())
|
1647
|
+
{
|
1648
|
+
stream << ", ";
|
1649
|
+
}
|
1650
|
+
stream << iter->first << " => " << iter->second;
|
1651
|
+
}
|
1652
|
+
|
1653
|
+
stream << "}";
|
1654
|
+
return stream.str();
|
1655
|
+
});
|
1656
|
+
}
|
1657
|
+
else
|
1658
|
+
{
|
1659
|
+
klass_.define_method("to_s", [](const T& unordered_map)
|
1660
|
+
{
|
1661
|
+
return "[Not printable]";
|
1662
|
+
});
|
1663
|
+
}
|
1664
|
+
}
|
1665
|
+
|
1666
|
+
private:
|
1667
|
+
Data_Type<T> klass_;
|
1668
|
+
};
|
1669
|
+
} // namespace
|
1670
|
+
|
1671
|
+
template<typename T>
|
1672
|
+
Data_Type<T> define_unordered_map_under(Object module, std::string name)
|
1673
|
+
{
|
1674
|
+
if (detail::Registries::instance.types.isDefined<T>())
|
1675
|
+
{
|
1676
|
+
// If the unordered_map has been previously seen it will be registered but may
|
1677
|
+
// not be associated with the constant Module::<name>
|
1678
|
+
module.const_set_maybe(name, Data_Type<T>().klass());
|
1679
|
+
return Data_Type<T>();
|
1680
|
+
}
|
1681
|
+
|
1682
|
+
Data_Type<T> result = define_class_under<detail::intrinsic_type<T>>(module, name.c_str());
|
1683
|
+
stl::UnorderedMapHelper helper(result);
|
1684
|
+
return result;
|
1685
|
+
}
|
1686
|
+
|
1687
|
+
template<typename T>
|
1688
|
+
Data_Type<T> define_unordered_map(std::string name)
|
1689
|
+
{
|
1690
|
+
if (detail::Registries::instance.types.isDefined<T>())
|
1691
|
+
{
|
1692
|
+
// If the unordered_map has been previously seen it will be registered but may
|
1693
|
+
// not be associated with the constant Object::<name>
|
1694
|
+
Object(rb_cObject).const_set_maybe(name, Data_Type<T>().klass());
|
1695
|
+
return Data_Type<T>();
|
1696
|
+
}
|
1697
|
+
|
1698
|
+
Data_Type<T> result = define_class<detail::intrinsic_type<T>>(name.c_str());
|
1699
|
+
stl::UnorderedMapHelper<T> helper(result);
|
1700
|
+
return result;
|
1701
|
+
}
|
1702
|
+
|
1703
|
+
template<typename T>
|
1704
|
+
Data_Type<T> define_unordered_map_auto()
|
1705
|
+
{
|
1706
|
+
std::string klassName = detail::makeClassName(typeid(T));
|
1707
|
+
Module rb_mRice = define_module("Rice");
|
1708
|
+
Module rb_munordered_map = define_module_under(rb_mRice, "Std");
|
1709
|
+
return define_unordered_map_under<T>(rb_munordered_map, klassName);
|
1710
|
+
}
|
1711
|
+
|
1712
|
+
namespace detail
|
1713
|
+
{
|
1714
|
+
template<typename T, typename U>
|
1715
|
+
struct Type<std::unordered_map<T, U>>
|
1716
|
+
{
|
1717
|
+
static bool verify()
|
1718
|
+
{
|
1719
|
+
Type<T>::verify();
|
1720
|
+
Type<U>::verify();
|
1721
|
+
|
1722
|
+
if (!detail::Registries::instance.types.isDefined<std::unordered_map<T, U>>())
|
1723
|
+
{
|
1724
|
+
define_unordered_map_auto<std::unordered_map<T, U>>();
|
1725
|
+
}
|
1726
|
+
|
1727
|
+
return true;
|
1728
|
+
}
|
1729
|
+
};
|
1730
|
+
|
1731
|
+
template<typename T, typename U>
|
1732
|
+
struct UnorderedMapFromHash
|
1733
|
+
{
|
1734
|
+
static int convertPair(VALUE key, VALUE value, VALUE user_data)
|
1735
|
+
{
|
1736
|
+
std::unordered_map<T, U>* result = (std::unordered_map<T, U>*)(user_data);
|
1737
|
+
|
1738
|
+
// This method is being called from Ruby so we cannot let any C++
|
1739
|
+
// exceptions propogate back to Ruby
|
1740
|
+
return cpp_protect([&]
|
1741
|
+
{
|
1742
|
+
result->operator[](From_Ruby<T>().convert(key)) = From_Ruby<U>().convert(value);
|
1743
|
+
return ST_CONTINUE;
|
1744
|
+
});
|
1745
|
+
}
|
1746
|
+
|
1747
|
+
static std::unordered_map<T, U> convert(VALUE value)
|
1748
|
+
{
|
1749
|
+
std::unordered_map<T, U> result;
|
1750
|
+
VALUE user_data = (VALUE)(&result);
|
1751
|
+
|
1752
|
+
// MSVC needs help here, but g++ does not
|
1753
|
+
using Rb_Hash_ForEach_T = void(*)(VALUE, int(*)(VALUE, VALUE, VALUE), VALUE);
|
1754
|
+
detail::protect<Rb_Hash_ForEach_T>(rb_hash_foreach, value, convertPair, user_data);
|
1755
|
+
|
1756
|
+
return result;
|
1757
|
+
}
|
1758
|
+
};
|
1759
|
+
|
1760
|
+
template<typename T, typename U>
|
1761
|
+
class From_Ruby<std::unordered_map<T, U>>
|
1762
|
+
{
|
1763
|
+
public:
|
1764
|
+
From_Ruby() = default;
|
1765
|
+
|
1766
|
+
explicit From_Ruby(Arg * arg) : arg_(arg)
|
1767
|
+
{
|
1768
|
+
}
|
1769
|
+
|
1770
|
+
std::unordered_map<T, U> convert(VALUE value)
|
1771
|
+
{
|
1772
|
+
switch (rb_type(value))
|
1773
|
+
{
|
1774
|
+
case T_DATA:
|
1775
|
+
{
|
1776
|
+
// This is a wrapped unordered_map (hopefully!)
|
1777
|
+
return *Data_Object<std::unordered_map<T, U>>::from_ruby(value);
|
1778
|
+
}
|
1779
|
+
case T_HASH:
|
1780
|
+
{
|
1781
|
+
// If this an Ruby hash and the unordered_mapped type is copyable
|
1782
|
+
if constexpr (std::is_default_constructible_v<U>)
|
1783
|
+
{
|
1784
|
+
return UnorderedMapFromHash<T, U>::convert(value);
|
1785
|
+
}
|
1786
|
+
}
|
1787
|
+
case T_NIL:
|
1788
|
+
{
|
1789
|
+
if (this->arg_ && this->arg_->hasDefaultValue())
|
1790
|
+
{
|
1791
|
+
return this->arg_->template defaultValue<std::unordered_map<T, U>>();
|
1792
|
+
}
|
1793
|
+
}
|
1794
|
+
default:
|
1795
|
+
{
|
1796
|
+
throw Exception(rb_eTypeError, "wrong argument type %s (expected % s)",
|
1797
|
+
detail::protect(rb_obj_classname, value), "std::unordered_map");
|
1798
|
+
}
|
1799
|
+
}
|
1800
|
+
}
|
1801
|
+
|
1802
|
+
private:
|
1803
|
+
Arg* arg_ = nullptr;
|
1804
|
+
};
|
1805
|
+
|
1806
|
+
template<typename T, typename U>
|
1807
|
+
class From_Ruby<std::unordered_map<T, U>&>
|
1808
|
+
{
|
1809
|
+
public:
|
1810
|
+
From_Ruby() = default;
|
1811
|
+
|
1812
|
+
explicit From_Ruby(Arg * arg) : arg_(arg)
|
1813
|
+
{
|
1814
|
+
}
|
1815
|
+
|
1816
|
+
std::unordered_map<T, U>& convert(VALUE value)
|
1817
|
+
{
|
1818
|
+
switch (rb_type(value))
|
1819
|
+
{
|
1820
|
+
case T_DATA:
|
1821
|
+
{
|
1822
|
+
// This is a wrapped unordered_map (hopefully!)
|
1823
|
+
return *Data_Object<std::unordered_map<T, U>>::from_ruby(value);
|
1824
|
+
}
|
1825
|
+
case T_HASH:
|
1826
|
+
{
|
1827
|
+
// If this an Ruby array and the unordered_map type is copyable
|
1828
|
+
if constexpr (std::is_default_constructible_v<std::unordered_map<T, U>>)
|
1829
|
+
{
|
1830
|
+
this->converted_ = UnorderedMapFromHash<T, U>::convert(value);
|
1831
|
+
return this->converted_;
|
1832
|
+
}
|
1833
|
+
}
|
1834
|
+
case T_NIL:
|
1835
|
+
{
|
1836
|
+
if (this->arg_ && this->arg_->hasDefaultValue())
|
1837
|
+
{
|
1838
|
+
return this->arg_->template defaultValue<std::unordered_map<T, U>>();
|
1839
|
+
}
|
1840
|
+
}
|
1841
|
+
default:
|
1842
|
+
{
|
1843
|
+
throw Exception(rb_eTypeError, "wrong argument type %s (expected % s)",
|
1844
|
+
detail::protect(rb_obj_classname, value), "std::unordered_map");
|
1845
|
+
}
|
1846
|
+
}
|
1847
|
+
}
|
1848
|
+
|
1849
|
+
private:
|
1850
|
+
Arg* arg_ = nullptr;
|
1851
|
+
std::unordered_map<T, U> converted_;
|
1852
|
+
};
|
1853
|
+
|
1854
|
+
template<typename T, typename U>
|
1855
|
+
class From_Ruby<std::unordered_map<T, U>*>
|
1856
|
+
{
|
1857
|
+
public:
|
1858
|
+
std::unordered_map<T, U>* convert(VALUE value)
|
1859
|
+
{
|
1860
|
+
switch (rb_type(value))
|
1861
|
+
{
|
1862
|
+
case T_DATA:
|
1863
|
+
{
|
1864
|
+
// This is a wrapped unordered_map (hopefully!)
|
1865
|
+
return Data_Object<std::unordered_map<T, U>>::from_ruby(value);
|
1866
|
+
}
|
1867
|
+
case T_HASH:
|
1868
|
+
{
|
1869
|
+
// If this an Ruby array and the unordered_map type is copyable
|
1870
|
+
if constexpr (std::is_default_constructible_v<U>)
|
1871
|
+
{
|
1872
|
+
this->converted_ = UnorderedMapFromHash<T, U>::convert(value);
|
1873
|
+
return &this->converted_;
|
1874
|
+
}
|
1875
|
+
}
|
1876
|
+
default:
|
1877
|
+
{
|
1878
|
+
throw Exception(rb_eTypeError, "wrong argument type %s (expected % s)",
|
1879
|
+
detail::protect(rb_obj_classname, value), "std::unordered_map");
|
1880
|
+
}
|
1881
|
+
}
|
1882
|
+
}
|
1883
|
+
|
1884
|
+
private:
|
1885
|
+
std::unordered_map<T, U> converted_;
|
1886
|
+
};
|
1887
|
+
}
|
1888
|
+
}
|
1889
|
+
|
1890
|
+
// ========= vector.hpp =========
|
1891
|
+
|
1892
|
+
|
1893
|
+
namespace Rice
|
1894
|
+
{
|
1895
|
+
template<typename T>
|
1896
|
+
Data_Type<T> define_vector(std::string name);
|
1897
|
+
|
1898
|
+
template<typename T>
|
1899
|
+
Data_Type<T> define_vector_under(Object module, std::string name);
|
1900
|
+
}
|
1901
|
+
|
1902
|
+
|
1903
|
+
// --------- vector.ipp ---------
|
1904
|
+
|
1905
|
+
#include <sstream>
|
1906
|
+
#include <stdexcept>
|
1907
|
+
#include <type_traits>
|
1908
|
+
#include <vector>
|
1909
|
+
#include <variant>
|
1910
|
+
|
1911
|
+
namespace Rice
|
1912
|
+
{
|
1913
|
+
namespace stl
|
1914
|
+
{
|
1915
|
+
template<typename T>
|
1916
|
+
class VectorHelper
|
1917
|
+
{
|
1918
|
+
using Value_T = typename T::value_type;
|
1919
|
+
using Size_T = typename T::size_type;
|
1920
|
+
using Difference_T = typename T::difference_type;
|
1921
|
+
|
1922
|
+
public:
|
1923
|
+
VectorHelper(Data_Type<T> klass) : klass_(klass)
|
1924
|
+
{
|
1925
|
+
this->define_constructor();
|
1926
|
+
this->define_copyable_methods();
|
1927
|
+
this->define_constructable_methods();
|
1928
|
+
this->define_capacity_methods();
|
1929
|
+
this->define_access_methods();
|
1930
|
+
this->define_comparable_methods();
|
1931
|
+
this->define_modify_methods();
|
1932
|
+
this->define_enumerable();
|
1933
|
+
this->define_to_array();
|
1934
|
+
this->define_to_s();
|
1935
|
+
}
|
1936
|
+
|
1937
|
+
private:
|
1938
|
+
|
1939
|
+
// Helper method to translate Ruby indices to vector indices
|
1940
|
+
Difference_T normalizeIndex(Size_T size, Difference_T index, bool enforceBounds = false)
|
1941
|
+
{
|
1942
|
+
// Negative indices mean count from the right. Note that negative indices
|
1943
|
+
// wrap around!
|
1944
|
+
if (index < 0)
|
1945
|
+
{
|
1946
|
+
index = ((-index) % size);
|
1947
|
+
index = index > 0 ? size - index : index;
|
1948
|
+
}
|
1949
|
+
|
1950
|
+
if (enforceBounds && (index < 0 || index >= (Difference_T)size))
|
1951
|
+
{
|
1952
|
+
throw std::out_of_range("Invalid index: " + std::to_string(index));
|
659
1953
|
}
|
660
1954
|
|
661
1955
|
return index;
|
@@ -670,17 +1964,17 @@ namespace Rice
|
|
670
1964
|
{
|
671
1965
|
if constexpr (std::is_copy_constructible_v<Value_T>)
|
672
1966
|
{
|
673
|
-
klass_.define_method("copy", [](T&
|
1967
|
+
klass_.define_method("copy", [](T& vector) -> T
|
674
1968
|
{
|
675
|
-
return
|
1969
|
+
return vector;
|
676
1970
|
});
|
677
1971
|
}
|
678
1972
|
else
|
679
1973
|
{
|
680
|
-
klass_.define_method("copy", [](T&
|
1974
|
+
klass_.define_method("copy", [](T& vector) -> T
|
681
1975
|
{
|
682
1976
|
throw std::runtime_error("Cannot copy vectors with non-copy constructible types");
|
683
|
-
return
|
1977
|
+
return vector;
|
684
1978
|
});
|
685
1979
|
}
|
686
1980
|
}
|
@@ -693,7 +1987,7 @@ namespace Rice
|
|
693
1987
|
}
|
694
1988
|
else
|
695
1989
|
{
|
696
|
-
klass_.define_method("resize", [](const T&
|
1990
|
+
klass_.define_method("resize", [](const T& vector, Size_T newSize)
|
697
1991
|
{
|
698
1992
|
// Do nothing
|
699
1993
|
});
|
@@ -717,38 +2011,38 @@ namespace Rice
|
|
717
2011
|
void define_access_methods()
|
718
2012
|
{
|
719
2013
|
// Access methods
|
720
|
-
klass_.define_method("first", [](const T&
|
2014
|
+
klass_.define_method("first", [](const T& vector) -> std::optional<Value_T>
|
721
2015
|
{
|
722
|
-
if (
|
2016
|
+
if (vector.size() > 0)
|
723
2017
|
{
|
724
|
-
return
|
2018
|
+
return vector.front();
|
725
2019
|
}
|
726
2020
|
else
|
727
2021
|
{
|
728
2022
|
return std::nullopt;
|
729
2023
|
}
|
730
2024
|
})
|
731
|
-
.define_method("last", [](const T&
|
2025
|
+
.define_method("last", [](const T& vector) -> std::optional<Value_T>
|
732
2026
|
{
|
733
|
-
if (
|
2027
|
+
if (vector.size() > 0)
|
734
2028
|
{
|
735
|
-
return
|
2029
|
+
return vector.back();
|
736
2030
|
}
|
737
2031
|
else
|
738
2032
|
{
|
739
2033
|
return std::nullopt;
|
740
2034
|
}
|
741
2035
|
})
|
742
|
-
.define_method("[]", [this](const T&
|
2036
|
+
.define_method("[]", [this](const T& vector, Difference_T index) -> std::optional<Value_T>
|
743
2037
|
{
|
744
|
-
index = normalizeIndex(
|
745
|
-
if (index < 0 || index >= (Difference_T)
|
2038
|
+
index = normalizeIndex(vector.size(), index);
|
2039
|
+
if (index < 0 || index >= (Difference_T)vector.size())
|
746
2040
|
{
|
747
2041
|
return std::nullopt;
|
748
2042
|
}
|
749
2043
|
else
|
750
2044
|
{
|
751
|
-
return
|
2045
|
+
return vector[index];
|
752
2046
|
}
|
753
2047
|
});
|
754
2048
|
|
@@ -760,48 +2054,48 @@ namespace Rice
|
|
760
2054
|
{
|
761
2055
|
if constexpr (detail::is_comparable_v<Value_T>)
|
762
2056
|
{
|
763
|
-
klass_.define_method("delete", [](T&
|
2057
|
+
klass_.define_method("delete", [](T& vector, Value_T& element) -> std::optional<Value_T>
|
764
2058
|
{
|
765
|
-
auto iter = std::find(
|
766
|
-
if (iter ==
|
2059
|
+
auto iter = std::find(vector.begin(), vector.end(), element);
|
2060
|
+
if (iter == vector.end())
|
767
2061
|
{
|
768
2062
|
return std::nullopt;
|
769
2063
|
}
|
770
2064
|
else
|
771
2065
|
{
|
772
2066
|
Value_T result = *iter;
|
773
|
-
|
2067
|
+
vector.erase(iter);
|
774
2068
|
return result;
|
775
2069
|
}
|
776
2070
|
})
|
777
|
-
.define_method("include?", [](T&
|
2071
|
+
.define_method("include?", [](T& vector, Value_T& element)
|
778
2072
|
{
|
779
|
-
return std::find(
|
2073
|
+
return std::find(vector.begin(), vector.end(), element) != vector.end();
|
780
2074
|
})
|
781
|
-
.define_method("index", [](T&
|
2075
|
+
.define_method("index", [](T& vector, Value_T& element) -> std::optional<Difference_T>
|
782
2076
|
{
|
783
|
-
auto iter = std::find(
|
784
|
-
if (iter ==
|
2077
|
+
auto iter = std::find(vector.begin(), vector.end(), element);
|
2078
|
+
if (iter == vector.end())
|
785
2079
|
{
|
786
2080
|
return std::nullopt;
|
787
2081
|
}
|
788
2082
|
else
|
789
2083
|
{
|
790
|
-
return iter -
|
2084
|
+
return iter - vector.begin();
|
791
2085
|
}
|
792
2086
|
});
|
793
2087
|
}
|
794
2088
|
else
|
795
2089
|
{
|
796
|
-
klass_.define_method("delete", [](T&
|
2090
|
+
klass_.define_method("delete", [](T& vector, Value_T& element) -> std::optional<Value_T>
|
797
2091
|
{
|
798
2092
|
return std::nullopt;
|
799
2093
|
})
|
800
|
-
.define_method("include?", [](const T&
|
2094
|
+
.define_method("include?", [](const T& vector, Value_T& element)
|
801
2095
|
{
|
802
2096
|
return false;
|
803
2097
|
})
|
804
|
-
.define_method("index", [](const T&
|
2098
|
+
.define_method("index", [](const T& vector, Value_T& element) -> std::optional<Difference_T>
|
805
2099
|
{
|
806
2100
|
return std::nullopt;
|
807
2101
|
});
|
@@ -811,26 +2105,26 @@ namespace Rice
|
|
811
2105
|
void define_modify_methods()
|
812
2106
|
{
|
813
2107
|
klass_.define_method("clear", &T::clear)
|
814
|
-
.define_method("delete_at", [](T&
|
2108
|
+
.define_method("delete_at", [](T& vector, const size_t& pos)
|
815
2109
|
{
|
816
|
-
auto iter =
|
2110
|
+
auto iter = vector.begin() + pos;
|
817
2111
|
Value_T result = *iter;
|
818
|
-
|
2112
|
+
vector.erase(iter);
|
819
2113
|
return result;
|
820
2114
|
})
|
821
|
-
.define_method("insert", [this](T&
|
2115
|
+
.define_method("insert", [this](T& vector, Difference_T index, Value_T& element) -> T&
|
822
2116
|
{
|
823
|
-
index = normalizeIndex(
|
824
|
-
auto iter =
|
825
|
-
|
826
|
-
return
|
2117
|
+
index = normalizeIndex(vector.size(), index, true);
|
2118
|
+
auto iter = vector.begin() + index;
|
2119
|
+
vector.insert(iter, element);
|
2120
|
+
return vector;
|
827
2121
|
})
|
828
|
-
.define_method("pop", [](T&
|
2122
|
+
.define_method("pop", [](T& vector) -> std::optional<Value_T>
|
829
2123
|
{
|
830
|
-
if (
|
2124
|
+
if (vector.size() > 0)
|
831
2125
|
{
|
832
|
-
Value_T result =
|
833
|
-
|
2126
|
+
Value_T result = vector.back();
|
2127
|
+
vector.pop_back();
|
834
2128
|
return result;
|
835
2129
|
}
|
836
2130
|
else
|
@@ -838,16 +2132,16 @@ namespace Rice
|
|
838
2132
|
return std::nullopt;
|
839
2133
|
}
|
840
2134
|
})
|
841
|
-
.define_method("push", [](T&
|
2135
|
+
.define_method("push", [](T& vector, Value_T& element) -> T&
|
842
2136
|
{
|
843
|
-
|
844
|
-
return
|
2137
|
+
vector.push_back(element);
|
2138
|
+
return vector;
|
845
2139
|
})
|
846
2140
|
.define_method("shrink_to_fit", &T::shrink_to_fit)
|
847
|
-
.define_method("[]=", [this](T&
|
2141
|
+
.define_method("[]=", [this](T& vector, Difference_T index, Value_T& element) -> Value_T&
|
848
2142
|
{
|
849
|
-
index = normalizeIndex(
|
850
|
-
|
2143
|
+
index = normalizeIndex(vector.size(), index, true);
|
2144
|
+
vector[index] = element;
|
851
2145
|
return element;
|
852
2146
|
});
|
853
2147
|
|
@@ -858,33 +2152,40 @@ namespace Rice
|
|
858
2152
|
void define_enumerable()
|
859
2153
|
{
|
860
2154
|
// Add enumerable support
|
861
|
-
klass_.
|
862
|
-
|
863
|
-
|
864
|
-
|
865
|
-
|
866
|
-
|
867
|
-
|
868
|
-
|
869
|
-
|
870
|
-
|
2155
|
+
klass_.template define_iterator<typename T::iterator(T::*)()>(&T::begin, &T::end);
|
2156
|
+
}
|
2157
|
+
|
2158
|
+
void define_to_array()
|
2159
|
+
{
|
2160
|
+
// Add enumerable support
|
2161
|
+
klass_.define_method("to_a", [](T& vector)
|
2162
|
+
{
|
2163
|
+
VALUE result = rb_ary_new();
|
2164
|
+
std::for_each(vector.begin(), vector.end(), [&result](const Value_T& element)
|
2165
|
+
{
|
2166
|
+
VALUE value = detail::To_Ruby<Value_T&>().convert(element);
|
2167
|
+
rb_ary_push(result, value);
|
2168
|
+
});
|
2169
|
+
|
2170
|
+
return result;
|
2171
|
+
}, Return().setValue());
|
871
2172
|
}
|
872
2173
|
|
873
2174
|
void define_to_s()
|
874
2175
|
{
|
875
2176
|
if constexpr (detail::is_ostreamable_v<Value_T>)
|
876
2177
|
{
|
877
|
-
klass_.define_method("to_s", [](const T&
|
2178
|
+
klass_.define_method("to_s", [](const T& vector)
|
878
2179
|
{
|
879
|
-
auto iter =
|
880
|
-
auto finish =
|
2180
|
+
auto iter = vector.begin();
|
2181
|
+
auto finish = vector.size() > 1000 ? vector.begin() + 1000 : vector.end();
|
881
2182
|
|
882
2183
|
std::stringstream stream;
|
883
2184
|
stream << "[";
|
884
2185
|
|
885
2186
|
for (; iter != finish; iter++)
|
886
2187
|
{
|
887
|
-
if (iter ==
|
2188
|
+
if (iter == vector.begin())
|
888
2189
|
{
|
889
2190
|
stream << *iter;
|
890
2191
|
}
|
@@ -900,7 +2201,7 @@ namespace Rice
|
|
900
2201
|
}
|
901
2202
|
else
|
902
2203
|
{
|
903
|
-
klass_.define_method("to_s", [](const T&
|
2204
|
+
klass_.define_method("to_s", [](const T& vector)
|
904
2205
|
{
|
905
2206
|
return "[Not printable]";
|
906
2207
|
});
|
@@ -915,9 +2216,13 @@ namespace Rice
|
|
915
2216
|
template<typename T>
|
916
2217
|
Data_Type<T> define_vector_under(Object module, std::string name)
|
917
2218
|
{
|
918
|
-
if (detail::
|
2219
|
+
if (detail::Registries::instance.types.isDefined<T>())
|
919
2220
|
{
|
920
|
-
|
2221
|
+
// If the vector has been previously seen it will be registered but may
|
2222
|
+
// not be associated with the constant Module::<name>
|
2223
|
+
module.const_set_maybe(name, Data_Type<T>().klass());
|
2224
|
+
|
2225
|
+
return Data_Type<T>();
|
921
2226
|
}
|
922
2227
|
|
923
2228
|
Data_Type<T> result = define_class_under<detail::intrinsic_type<T>>(module, name.c_str());
|
@@ -928,9 +2233,13 @@ namespace Rice
|
|
928
2233
|
template<typename T>
|
929
2234
|
Data_Type<T> define_vector(std::string name)
|
930
2235
|
{
|
931
|
-
if (detail::
|
2236
|
+
if (detail::Registries::instance.types.isDefined<T>())
|
932
2237
|
{
|
933
|
-
|
2238
|
+
// If the vector has been previously seen it will be registered but may
|
2239
|
+
// not be associated with the constant Module::<name>
|
2240
|
+
Object(rb_cObject).const_set_maybe(name, Data_Type<T>().klass());
|
2241
|
+
|
2242
|
+
return Data_Type<T>();
|
934
2243
|
}
|
935
2244
|
|
936
2245
|
Data_Type<T> result = define_class<detail::intrinsic_type<T>>(name.c_str());
|
@@ -956,7 +2265,7 @@ namespace Rice
|
|
956
2265
|
{
|
957
2266
|
Type<T>::verify();
|
958
2267
|
|
959
|
-
if (!detail::
|
2268
|
+
if (!detail::Registries::instance.types.isDefined<std::vector<T>>())
|
960
2269
|
{
|
961
2270
|
define_vector_auto<std::vector<T>>();
|
962
2271
|
}
|
@@ -968,7 +2277,7 @@ namespace Rice
|
|
968
2277
|
template<typename T>
|
969
2278
|
std::vector<T> vectorFromArray(VALUE value)
|
970
2279
|
{
|
971
|
-
|
2280
|
+
long length = protect(rb_array_len, value);
|
972
2281
|
std::vector<T> result(length);
|
973
2282
|
|
974
2283
|
for (long i = 0; i < length; i++)
|