rcx 0.1.0 → 0.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 59b6c06f1c9ff032b2b70878a6368e4d83e4a8370a19fb2d51b5420831a0211e
4
- data.tar.gz: 78d982e5b911b4bb891681bc06ff9da85fb57809076f8f508933269dc9d9a6e0
3
+ metadata.gz: 291d8e72a9ddea530ab4aa2d585c5d97406603a97d5b72a75caeb2cb79eea8aa
4
+ data.tar.gz: 570ed623fd7e76df3df6808559d1195476690b2983889e5fc76945e61a1936a8
5
5
  SHA512:
6
- metadata.gz: 59354a09ceac21baa60fc1ef1f645d56645b61ccc707eb88f2c533c14d1c7b64f4c57223faebf02bea62c1f351aba4f00819eb9f2d20011cf645c2dc8807a210
7
- data.tar.gz: ed6563dd44037828a5f620b27a54f2d3714e7e5cdcfe902f913da8a18c4ecb2bbf5ed32bd34c574feb818b7ecc2a80c11526fb5dde21fb4f8217a6089c6b850f
6
+ metadata.gz: b33290743f173becad0b8736cc6c50c34d5cd3057aedd07dca97500021f64dd7e900b00fc34f35bd9291b46aad91761d4a212b51b339931391040cd6f9461430
7
+ data.tar.gz: 2322cb7a7e48eb414cce22352b36f9bd8d21ee481bb742f4f3b60ac7a75715277ddc6c39daa43a4e59e2e6354d304935f4995607175c257e6abbda44930024c4
data/CHANGELOG.md CHANGED
@@ -1,4 +1,8 @@
1
1
  ## UNRELEASED
2
2
 
3
+ ## v0.2.0 (2025-07-09)
4
+ - Added `rcx::IO`
5
+ - Added `rcx::Exception::new_from_errno`
6
+
3
7
  ## v0.1.0 (2025-02-04)
4
8
  - Initial release
data/Rakefile CHANGED
@@ -37,3 +37,8 @@ end
37
37
  task :yarn_install do
38
38
  sh 'yarn', 'install'
39
39
  end
40
+
41
+ desc 'Format the source code'
42
+ task :format => FileList['**/*.hpp'] do |t|
43
+ sh 'clang-format', '-i', *t.prerequisites
44
+ end
data/examples/class.cpp CHANGED
@@ -5,7 +5,7 @@
5
5
  void Init_examples_class() {
6
6
  using namespace rcx::arg;
7
7
 
8
- auto ruby = rcx::Ruby();
8
+ auto ruby = rcx::Ruby::get();
9
9
  auto cls =
10
10
  ruby.define_class("Example")
11
11
  .define_method(
@@ -60,6 +60,7 @@ namespace rcx {
60
60
  #ifdef RCX_IO_BUFFER
61
61
  class IOBuffer;
62
62
  #endif
63
+ class IO;
63
64
  }
64
65
  using namespace value;
65
66
 
@@ -256,6 +257,9 @@ namespace rcx {
256
257
  IOBuffer convert(Value value);
257
258
  };
258
259
  #endif
260
+ template <> struct FromValue<IO> {
261
+ IO convert(Value value);
262
+ };
259
263
 
260
264
  #define RCX_DECLARE_CLASS_CONV(CLS) \
261
265
  template <> struct FromValue<ClassT<CLS>> { \
@@ -269,6 +273,7 @@ namespace rcx {
269
273
  RCX_DECLARE_CLASS_CONV(String);
270
274
  RCX_DECLARE_CLASS_CONV(Array);
271
275
  RCX_DECLARE_CLASS_CONV(Exception);
276
+ RCX_DECLARE_CLASS_CONV(IO);
272
277
  #ifdef RCX_IO_BUFFER
273
278
  RCX_DECLARE_CLASS_CONV(IOBuffer);
274
279
  #endif
@@ -358,8 +363,10 @@ namespace rcx {
358
363
  ///
359
364
  constexpr inline ArgSplat arg_splat;
360
365
  /// Block.
366
+ ///
361
367
  constexpr inline Block block;
362
368
  /// Optional block.
369
+ ///
363
370
  constexpr inline BlockOpt block_opt;
364
371
  }
365
372
 
@@ -577,28 +584,40 @@ namespace rcx {
577
584
 
578
585
  /// Defines a module under this module.
579
586
  ///
587
+ /// Creates a new module nested under this module. The module will be accessible
588
+ /// as a constant within this module's namespace.
589
+ ///
580
590
  /// @warning Modules defined this way will be never garbage-collected.
581
591
  ///
582
- /// @param name Name of the module.
592
+ /// @param name Name of the module to define.
583
593
  /// @return The newly defined module.
584
594
  Module define_module(concepts::Identifier auto &&name) const;
585
595
 
586
- /// Defines a class under this module.
596
+ /// Defines a class under this module with a specified superclass.
597
+ ///
598
+ /// Creates a new class nested under this module with the given superclass.
599
+ /// The class will be accessible as a constant within this module's namespace.
587
600
  ///
588
601
  /// @warning Classes defined this way will be never garbage-collected.
589
602
  ///
590
- /// @param name Name of the class.
591
- /// @param superclass The new class will be a subclass of this class.
603
+ /// @tparam T The C++ type of the class.
604
+ /// @tparam S The C++ type of the superclass.
605
+ /// @param name Name of the class to define.
606
+ /// @param superclass The class that will serve as the superclass.
592
607
  /// @return The newly defined class.
593
608
  template <typename T = Value, typename S>
594
609
  ClassT<T> define_class(concepts::Identifier auto &&name, ClassT<S> superclass) const;
595
610
 
596
- /// Defines a subclass of Object under this module.
611
+ /// Defines a class under this module inheriting from Object.
612
+ ///
613
+ /// Creates a new class nested under this module, with Object as the superclass.
614
+ /// The class will be accessible as a constant within this module's namespace.
597
615
  ///
598
616
  /// @warning Classes defined this way will be never garbage-collected.
599
617
  ///
600
- /// @param name Name of the class.
601
- /// @return The newly created class.
618
+ /// @tparam T The C++ type of the class.
619
+ /// @param name Name of the class to define.
620
+ /// @return The newly defined class.
602
621
  template <typename T = Value> ClassT<T> define_class(concepts::Identifier auto &&name) const;
603
622
 
604
623
  /// Defines an instance method.
@@ -639,7 +658,7 @@ namespace rcx {
639
658
 
640
659
  /// Creates an anonymous module.
641
660
  ///
642
- /// @return The newly created module.
661
+ /// @return The newly created anonymous module.
643
662
  static Module new_module();
644
663
  };
645
664
 
@@ -713,59 +732,142 @@ namespace rcx {
713
732
  ClassT<T> define_copy_constructor() const
714
733
  requires std::copy_constructible<T>;
715
734
 
716
- /// Creates a new class.
735
+ /// Creates an anonymous class inheriting from Object.
736
+ ///
737
+ /// Creates a new anonymous class that is not assigned to any constant,
738
+ /// with Object as the superclass. The class can be used dynamically
739
+ /// or assigned to constants later.
717
740
  ///
718
- /// @return The newly created class.
741
+ /// @return The newly created anonymous class.
719
742
  static Class new_class();
720
743
 
721
- /// Creates a new class with a superclass.
744
+ /// Creates an anonymous class with a specified superclass.
745
+ ///
746
+ /// Creates a new anonymous class that is not assigned to any constant,
747
+ /// with the given class as the superclass. The class can be used dynamically
748
+ /// or assigned to constants later.
722
749
  ///
723
- /// @param superclass The new class will be a subclass of this class.
724
- /// @return The newly created class.
750
+ /// @tparam S The C++ type of the superclass.
751
+ /// @param superclass The class that will serve as the superclass.
752
+ /// @return The newly created anonymous class.
725
753
  template <typename S> static ClassT<S> new_class(ClassT<S> superclass);
726
754
  };
727
755
 
756
+ /// Represents a Ruby `Symbol`.
757
+ ///
758
+ /// A `Symbol` object represents a name inside the Ruby interpreter.
728
759
  class Symbol: public ValueT<Symbol, Value> {
729
760
  public:
730
761
  using ValueT<Symbol, Value>::ValueT;
731
- template <size_t N> explicit Symbol(char const (&)[N]) noexcept;
762
+
763
+ /// Creates a `Symbol` from a C string literal.
764
+ ///
765
+ /// @param s The C string literal.
766
+ template <size_t N> explicit Symbol(char const (&s)[N]) noexcept;
767
+
768
+ /// Creates a `Symbol` from a string view.
769
+ ///
770
+ /// @param sv The string view.
732
771
  explicit Symbol(std::string_view sv) noexcept;
733
772
 
734
- /**
735
- * Returns Ruby-internal ID.
736
- *
737
- * The ID returned by this method may be dynamic and subject to garbage collection.
738
- * So do not store, whether on stack or in heap.
739
- */
773
+ /// Returns Ruby-internal ID.
774
+ ///
775
+ /// @warning The ID returned by this method may be dynamic and subject to garbage collection.
776
+ /// So do not store, whether on stack or in heap.
777
+ /// @return The Ruby-internal ID.
740
778
  ID as_ID() const noexcept;
741
779
  };
742
780
 
781
+ /// Represents a Ruby `String`.
782
+ ///
743
783
  class String: public ValueT<String, Value> {
744
784
  public:
745
785
  using ValueT<String, Value>::ValueT;
746
786
 
787
+ /// Creates a deduped frozen `String` from a C++ string-like object.
788
+ ///
789
+ /// @param s The C++ string-like object.
790
+ /// @return The created deduped frozen `String`.
747
791
  template <concepts::StringLike S> static String intern_from(S &&s);
792
+
793
+ /// Creates a deduped frozen `String` from a C string.
794
+ ///
795
+ /// @param s The C string.
796
+ /// @return The created deduped frozen `String`.
748
797
  template <concepts::CharLike CharT> static String intern_from(CharT const *RCX_Nonnull s);
798
+
799
+ /// Creates a mutable `String` from a C++ string-like object.
800
+ ///
801
+ /// @param s The C++ string-like object.
802
+ /// @return The created mutable `String`.
749
803
  template <concepts::StringLike S> static String copy_from(S &&s);
804
+
805
+ /// Creates a mutable `String` from a C string.
806
+ ///
807
+ /// @param s The C string.
808
+ /// @return The created mutable `String`.
750
809
  template <concepts::CharLike CharT> static String copy_from(CharT const *RCX_Nonnull s);
751
810
 
811
+ /// Returns the length of the string in octets.
812
+ ///
813
+ /// @return The length of the string in octets.
752
814
  size_t size() const noexcept;
815
+
816
+ /// Returns a mutable pointer to the string's content.
817
+ ///
818
+ /// @return A mutable pointer to the string's content.
819
+ /// @throws FrozenError When the string is frozen.
753
820
  char *RCX_Nonnull data() const;
821
+
822
+ /// Returns a const pointer to the string's content.
823
+ ///
824
+ /// @return A const pointer to the string's content.
754
825
  char const *RCX_Nonnull cdata() const noexcept;
826
+
827
+ /// Returns a string view of the string's content.
828
+ ///
829
+ /// @return A string view of the string's content.
755
830
  explicit operator std::string_view() const noexcept;
756
831
 
832
+ /// Locks the string to prevent it from being freed or relocated.
833
+ ///
834
+ /// @return The locked string.
757
835
  String lock() const;
836
+
837
+ /// Unlocks the string to allow it to be modified.
838
+ ///
839
+ /// @return The unlocked string.
758
840
  String unlock() const;
759
841
  };
760
842
 
843
+ /// Represents a Ruby `Array`.
844
+ ///
761
845
  class Array: public ValueT<Array, Value> {
762
846
  public:
763
847
  using ValueT<Array, Value>::ValueT;
764
848
 
849
+ /// Returns the number of elements in the array.
850
+ ///
851
+ /// @return The number of elements in the array.
765
852
  size_t size() const noexcept;
853
+
854
+ /// Returns the element at the given index.
855
+ ///
856
+ /// @tparam T The type to convert the element to.
857
+ /// @param i The index of the element.
858
+ /// @return The element at the given index.
766
859
  template <concepts::ConvertibleFromValue T = Value> decltype(auto) at(size_t i) const;
860
+
861
+ /// Returns the element at the given index.
862
+ ///
863
+ /// @param i The index of the element.
864
+ /// @return The element at the given index.
767
865
  Value operator[](size_t i) const;
768
866
 
867
+ /// Creates a new `Array` from a contiguous range of values.
868
+ ///
869
+ /// @param elements The contiguous range of values.
870
+ /// @return The new `Array`.
769
871
  template <std::ranges::contiguous_range R>
770
872
  #ifdef HAVE_STD_IS_LAYOUT_COMPATIBLE
771
873
  requires std::is_layout_compatible_v<std::ranges::range_value_t<R>, ValueBase>
@@ -775,38 +877,131 @@ namespace rcx {
775
877
  #endif
776
878
  static Array new_from(R const &elements);
777
879
 
880
+ /// Creates a new `Array` from an initializer list of values.
881
+ ///
882
+ /// @param elements The initializer list of values.
883
+ /// @return The new `Array`.
778
884
  static Array new_from(std::initializer_list<ValueBase> elements);
779
885
 
886
+ /// Creates a new `Array` from a tuple of values.
887
+ ///
888
+ /// @param elements The tuple of values.
889
+ /// @return The new `Array`.
780
890
  template <std::derived_from<ValueBase>... T>
781
891
  static Array new_from(std::tuple<T...> const &elements);
782
892
 
893
+ /// Creates a new empty `Array`.
894
+ ///
895
+ /// @return The new empty `Array`.
783
896
  static Array new_array();
897
+
898
+ /// Creates a new empty `Array` with the given capacity.
899
+ ///
900
+ /// @param capacity The capacity of the new `Array`.
901
+ /// @return The new empty `Array`.
784
902
  static Array new_array(long capacity);
785
903
 
904
+ /// Appends a value to the end of the array.
905
+ ///
906
+ /// @param value The value to append.
907
+ /// @return The array itself.
786
908
  template <concepts::ConvertibleIntoValue T = Value> Array push_back(T value) const;
909
+
910
+ /// Removes and returns the last element of the array.
911
+ ///
912
+ /// @tparam T The type to convert the element to.
913
+ /// @return The last element of the array.
787
914
  template <concepts::ConvertibleFromValue T = Value> T pop_back() const;
915
+
916
+ /// Prepends a value to the beginning of the array.
917
+ ///
918
+ /// @param value The value to prepend.
919
+ /// @return The array itself.
788
920
  template <concepts::ConvertibleIntoValue T = Value> Array push_front(T value) const;
921
+
922
+ /// Removes and returns the first element of the array.
923
+ ///
924
+ /// @tparam T The type to convert the element to.
925
+ /// @return The first element of the array.
789
926
  template <concepts::ConvertibleFromValue T = Value> T pop_front() const;
790
927
  };
791
928
 
929
+ /// Represents a Ruby `Proc`.
930
+ ///
792
931
  class Proc: public ValueT<Proc, Value> {
793
932
  public:
794
933
  using ValueT<Proc, Value>::ValueT;
795
934
 
935
+ /// Checks if this `Proc` is a lambda.
936
+ ///
937
+ /// @return `true` if this `Proc` is a lambda, `false` otherwise.
796
938
  bool is_lambda() const;
939
+
940
+ /// Calls this `Proc` with the given arguments.
941
+ ///
942
+ /// @param args The arguments to pass to the `Proc`.
943
+ /// @return The result of calling the `Proc`.
797
944
  Value call(Array args) const;
798
945
  };
799
946
 
947
+ /// Represents a Ruby exception.
948
+ ///
949
+ /// This class provides functionality for creating and formatting exceptions in Ruby.
800
950
  class Exception: public ValueT<Exception, Value> {
801
951
  public:
802
952
  using ValueT<Exception, Value>::ValueT;
803
953
 
954
+ /// Creates a formatted exception of the specified type.
955
+ ///
956
+ /// This method creates an exception instance with a formatted message using C++20 std::format
957
+ /// syntax. The message is created as a frozen string and passed to the exception constructor.
958
+ ///
959
+ /// @tparam E The type of exception class (must be derived from Exception).
960
+ /// @tparam Args The argument types for the format string.
961
+ /// @param cls The exception class to instantiate.
962
+ /// @param fmt The format string (follows std::format syntax).
963
+ /// @param args The arguments to format into the string.
964
+ /// @return A new exception instance with the formatted message.
804
965
  template <std::derived_from<Exception> E, typename... Args>
805
966
  static E format(ClassT<E> cls, std::format_string<Args...> fmt, Args &&...args);
967
+
968
+ /// Creates a `SystemCallError` from an errno value.
969
+ ///
970
+ /// This method creates a SystemCallError exception based on the provided errno value.
971
+ /// The errno is converted to an appropriate Ruby exception message automatically.
972
+ ///
973
+ /// @param message An optional message to be appended to the error message.
974
+ /// @param err The errno value (defaults to the current errno).
975
+ /// @return The newly created `SystemCallError`.
976
+ static Exception new_from_errno(char const *RCX_Nullable message = nullptr, int err = errno);
977
+ };
978
+
979
+ /// Represents a Ruby `IO`.
980
+ ///
981
+ class IO: public ValueT<IO, Value> {
982
+ public:
983
+ using ValueT<IO, Value>::ValueT;
984
+
985
+ /// The file descriptor of the IO object.
986
+ ///
987
+ /// @return The file descriptor.
988
+ /// @throws IOError If file IO object is closed.
989
+ int descriptor() const;
990
+
991
+ /// Checks if the IO object is opened for reading.
992
+ ///
993
+ /// @throws IOError If the IO object is not readable.
994
+ void check_readable() const;
995
+
996
+ /// Checks if the IO object is opened for writing.
997
+ ///
998
+ /// @throws IOError If the IO object is not writable.
999
+ void check_writable() const;
806
1000
  };
807
1001
 
808
1002
  #ifdef RCX_IO_BUFFER
809
1003
  /// Represents an `IO::Buffer` object.
1004
+ ///
810
1005
  class IOBuffer: public ValueT<IOBuffer, Value> {
811
1006
  public:
812
1007
  using ValueT<IOBuffer, Value>::ValueT;
@@ -958,6 +1153,10 @@ namespace rcx {
958
1153
  ///
959
1154
  inline value::ClassT<value::Exception> const Exception =
960
1155
  detail::unsafe_coerce<value::ClassT<value::Exception>>(::rb_eException);
1156
+ /// `StandardError` class
1157
+ ///
1158
+ inline value::ClassT<value::Exception> const StandardError =
1159
+ detail::unsafe_coerce<value::ClassT<value::Exception>>(::rb_eStandardError);
961
1160
  /// `SystemExit` class
962
1161
  ///
963
1162
  inline value::ClassT<value::Exception> const SystemExit =
@@ -970,30 +1169,22 @@ namespace rcx {
970
1169
  ///
971
1170
  inline value::ClassT<value::Exception> const SignalException =
972
1171
  detail::unsafe_coerce<value::ClassT<value::Exception>>(::rb_eSignal);
973
- /// `StandardError` class
974
- ///
975
- inline value::ClassT<value::Exception> const StandardError =
976
- detail::unsafe_coerce<value::ClassT<value::Exception>>(::rb_eStandardError);
977
- /// `RuntimeError` class
978
- ///
979
- inline value::ClassT<value::Exception> const RuntimeError =
980
- detail::unsafe_coerce<value::ClassT<value::Exception>>(::rb_eRuntimeError);
981
- /// `FrozenError` class
982
- ///
983
- inline value::ClassT<value::Exception> const FrozenError =
984
- detail::unsafe_coerce<value::ClassT<value::Exception>>(::rb_eFrozenError);
985
- /// `TypeError` class
986
- ///
987
- inline value::ClassT<value::Exception> const TypeError =
988
- detail::unsafe_coerce<value::ClassT<value::Exception>>(::rb_eTypeError);
989
1172
  /// `ArgumentError` class
990
1173
  ///
991
1174
  inline value::ClassT<value::Exception> const ArgumentError =
992
1175
  detail::unsafe_coerce<value::ClassT<value::Exception>>(::rb_eArgError);
1176
+ /// `EOFError` class
1177
+ ///
1178
+ inline value::ClassT<value::Exception> const EOFError =
1179
+ detail::unsafe_coerce<value::ClassT<value::Exception>>(::rb_eEOFError);
993
1180
  /// `IndexError` class
994
1181
  ///
995
1182
  inline value::ClassT<value::Exception> const IndexError =
996
1183
  detail::unsafe_coerce<value::ClassT<value::Exception>>(::rb_eIndexError);
1184
+ /// `StopIteration` class
1185
+ ///
1186
+ inline value::ClassT<value::Exception> const StopIteration =
1187
+ detail::unsafe_coerce<value::ClassT<value::Exception>>(::rb_eStopIteration);
997
1188
  /// `KeyError` class
998
1189
  ///
999
1190
  inline value::ClassT<value::Exception> const KeyError =
@@ -1002,26 +1193,38 @@ namespace rcx {
1002
1193
  ///
1003
1194
  inline value::ClassT<value::Exception> const RangeError =
1004
1195
  detail::unsafe_coerce<value::ClassT<value::Exception>>(::rb_eRangeError);
1005
- /// `NameError` class
1006
- ///
1007
- inline value::ClassT<value::Exception> const NameError =
1008
- detail::unsafe_coerce<value::ClassT<value::Exception>>(::rb_eNameError);
1009
- /// `EncodingError` class
1196
+ /// `IOError` class
1010
1197
  ///
1011
- inline value::ClassT<value::Exception> const EncodingError =
1012
- detail::unsafe_coerce<value::ClassT<value::Exception>>(::rb_eEncodingError);
1013
- /// `Encoding::CompatibilityError` class
1198
+ inline value::ClassT<value::Exception> const IOError =
1199
+ detail::unsafe_coerce<value::ClassT<value::Exception>>(::rb_eIOError);
1200
+ /// `RuntimeError` class
1014
1201
  ///
1015
- inline value::ClassT<value::Exception> const EncodingCompatibilityError =
1016
- detail::unsafe_coerce<value::ClassT<value::Exception>>(::rb_eEncCompatError);
1017
- /// `NoMethodError` class
1202
+ inline value::ClassT<value::Exception> const RuntimeError =
1203
+ detail::unsafe_coerce<value::ClassT<value::Exception>>(::rb_eRuntimeError);
1204
+ /// `FrozenError` class
1018
1205
  ///
1019
- inline value::ClassT<value::Exception> const NoMethodError =
1020
- detail::unsafe_coerce<value::ClassT<value::Exception>>(::rb_eNoMethodError);
1206
+ inline value::ClassT<value::Exception> const FrozenError =
1207
+ detail::unsafe_coerce<value::ClassT<value::Exception>>(::rb_eFrozenError);
1021
1208
  /// `SecurityError` class
1022
1209
  ///
1023
1210
  inline value::ClassT<value::Exception> const SecurityError =
1024
1211
  detail::unsafe_coerce<value::ClassT<value::Exception>>(::rb_eSecurityError);
1212
+ /// `SystemCallError` class
1213
+ ///
1214
+ inline value::ClassT<value::Exception> const SystemCallError =
1215
+ detail::unsafe_coerce<value::ClassT<value::Exception>>(::rb_eSystemCallError);
1216
+ /// `ThreadError` class
1217
+ ///
1218
+ inline value::ClassT<value::Exception> const ThreadError =
1219
+ detail::unsafe_coerce<value::ClassT<value::Exception>>(::rb_eThreadError);
1220
+ /// `TypeError` class
1221
+ ///
1222
+ inline value::ClassT<value::Exception> const TypeError =
1223
+ detail::unsafe_coerce<value::ClassT<value::Exception>>(::rb_eTypeError);
1224
+ /// `ZeroDivisionError` class
1225
+ ///
1226
+ inline value::ClassT<value::Exception> const ZeroDivisionError =
1227
+ detail::unsafe_coerce<value::ClassT<value::Exception>>(::rb_eZeroDivError);
1025
1228
  /// `NotImplementedError` class
1026
1229
  ///
1027
1230
  inline value::ClassT<value::Exception> const NotImplementedError =
@@ -1030,6 +1233,34 @@ namespace rcx {
1030
1233
  ///
1031
1234
  inline value::ClassT<value::Exception> const NoMemoryError =
1032
1235
  detail::unsafe_coerce<value::ClassT<value::Exception>>(::rb_eNoMemError);
1236
+ /// `NoMethodError` class
1237
+ ///
1238
+ inline value::ClassT<value::Exception> const NoMethodError =
1239
+ detail::unsafe_coerce<value::ClassT<value::Exception>>(::rb_eNoMethodError);
1240
+ /// `FloatDomainError` class
1241
+ ///
1242
+ inline value::ClassT<value::Exception> const FloatDomainError =
1243
+ detail::unsafe_coerce<value::ClassT<value::Exception>>(::rb_eFloatDomainError);
1244
+ /// `LocalJumpError` class
1245
+ ///
1246
+ inline value::ClassT<value::Exception> const LocalJumpError =
1247
+ detail::unsafe_coerce<value::ClassT<value::Exception>>(::rb_eLocalJumpError);
1248
+ /// `SystemStackError` class
1249
+ ///
1250
+ inline value::ClassT<value::Exception> const SystemStackError =
1251
+ detail::unsafe_coerce<value::ClassT<value::Exception>>(::rb_eSysStackError);
1252
+ /// `RegexpError` class
1253
+ ///
1254
+ inline value::ClassT<value::Exception> const RegexpError =
1255
+ detail::unsafe_coerce<value::ClassT<value::Exception>>(::rb_eRegexpError);
1256
+ /// `EncodingError` class
1257
+ ///
1258
+ inline value::ClassT<value::Exception> const EncodingError =
1259
+ detail::unsafe_coerce<value::ClassT<value::Exception>>(::rb_eEncodingError);
1260
+ /// `Encoding::CompatibilityError` class
1261
+ ///
1262
+ inline value::ClassT<value::Exception> const EncodingCompatibilityError =
1263
+ detail::unsafe_coerce<value::ClassT<value::Exception>>(::rb_eEncCompatError);
1033
1264
  /// `NoMatchingPatternError` class
1034
1265
  ///
1035
1266
  inline value::ClassT<value::Exception> const NoMatchingPatternError =
@@ -1042,6 +1273,10 @@ namespace rcx {
1042
1273
  ///
1043
1274
  inline value::ClassT<value::Exception> const ScriptError =
1044
1275
  detail::unsafe_coerce<value::ClassT<value::Exception>>(::rb_eScriptError);
1276
+ /// `NameError` class
1277
+ ///
1278
+ inline value::ClassT<value::Exception> const NameError =
1279
+ detail::unsafe_coerce<value::ClassT<value::Exception>>(::rb_eNameError);
1045
1280
  /// `SyntaxError` class
1046
1281
  ///
1047
1282
  inline value::ClassT<value::Exception> const SyntaxError =
@@ -1050,10 +1285,10 @@ namespace rcx {
1050
1285
  ///
1051
1286
  inline value::ClassT<value::Exception> const LoadError =
1052
1287
  detail::unsafe_coerce<value::ClassT<value::Exception>>(::rb_eLoadError);
1053
- /// `SystemCallError` class
1288
+ /// `Math::DomainError` class
1054
1289
  ///
1055
- inline value::ClassT<value::Exception> const SystemCallError =
1056
- detail::unsafe_coerce<value::ClassT<value::Exception>>(::rb_eSystemCallError);
1290
+ inline value::ClassT<value::Exception> const MathDomainError =
1291
+ detail::unsafe_coerce<value::ClassT<value::Exception>>(::rb_eMathDomainError);
1057
1292
 
1058
1293
  #ifdef RCX_IO_BUFFER
1059
1294
  /// `IO::Buffer` class
@@ -1224,27 +1459,67 @@ namespace rcx {
1224
1459
  };
1225
1460
  template <std::derived_from<ValueBase> T> Leak(T) -> Leak<T>;
1226
1461
 
1462
+ /// Provides access to Ruby's environment.
1463
+ ///
1227
1464
  class Ruby {
1465
+ private:
1466
+ Ruby() = default;
1467
+
1228
1468
  public:
1469
+ /// Defines a module at the top level.
1470
+ ///
1471
+ /// Creates a new module under Ruby's Object class. The module will be accessible
1472
+ /// as a top-level constant in Ruby code.
1473
+ ///
1474
+ /// @warning Modules defined this way will be never garbage-collected.
1475
+ ///
1476
+ /// @param name Name of the module to define.
1477
+ /// @return The newly defined module.
1229
1478
  Module define_module(concepts::Identifier auto &&name);
1230
1479
 
1480
+ /// Defines a class at the top level with a specified superclass.
1481
+ ///
1482
+ /// Creates a new class under Ruby's Object class with the given superclass.
1483
+ /// The class will be accessible as a top-level constant in Ruby code.
1484
+ ///
1485
+ /// @warning Classes defined this way will be never garbage-collected.
1486
+ ///
1487
+ /// @tparam T The C++ type of the class.
1488
+ /// @tparam S The C++ type of the superclass.
1489
+ /// @param name Name of the class to define.
1490
+ /// @param superclass The class that will serve as the superclass.
1491
+ /// @return The newly defined class.
1231
1492
  template <typename T = Value, typename S>
1232
1493
  ClassT<T> define_class(concepts::Identifier auto &&name, ClassT<S> superclass);
1233
1494
 
1495
+ /// Defines a class at the top level inheriting from Object.
1496
+ ///
1497
+ /// Creates a new class under Ruby's Object class, with Object as the superclass.
1498
+ /// The class will be accessible as a top-level constant in Ruby code.
1499
+ ///
1500
+ /// @warning Classes defined this way will be never garbage-collected.
1501
+ ///
1502
+ /// @tparam T The C++ type of the class.
1503
+ /// @param name Name of the class to define.
1504
+ /// @return The newly defined class.
1234
1505
  template <typename T = Value> ClassT<T> define_class(concepts::Identifier auto &&name);
1235
- };
1236
1506
 
1237
- namespace detail {
1238
- inline Ruby &unsafe_ruby() {
1239
- static Ruby ruby = {};
1240
- return ruby;
1241
- }
1242
-
1243
- inline Ruby &ruby() {
1244
- // TODO: check gvl
1245
- return unsafe_ruby();
1246
- }
1247
- }
1507
+ /// Gets the Ruby environment without GVL checking.
1508
+ ///
1509
+ /// @warning This method bypasses GVL checking and should only be used
1510
+ /// when you are certain the GVL is held or when calling from Ruby callbacks.
1511
+ /// @return Reference to the Ruby environment instance.
1512
+ static Ruby &unsafe_get();
1513
+
1514
+ /// Gets the Ruby environment with GVL checking.
1515
+ ///
1516
+ /// This method asserts that the Global VM Lock is held before returning
1517
+ /// the Ruby environment instance. This is the recommended way to access
1518
+ /// the Ruby environment from C++ code.
1519
+ ///
1520
+ /// @return Reference to the Ruby environment instance.
1521
+ static Ruby &get();
1522
+ };
1248
1523
  }
1249
1524
 
1250
1525
  namespace std {
@@ -13,6 +13,7 @@
13
13
 
14
14
  #include <ffi.h>
15
15
  #include <rcx/internal/rcx.hpp>
16
+ #include <ruby/io.h>
16
17
 
17
18
  #if HAVE_CXXABI_H
18
19
  #include <cxxabi.h>
@@ -119,12 +120,12 @@ namespace rcx {
119
120
  template <typename F> static NativeRbFunc *RCX_Nonnull alloc(F &&function) {
120
121
  return alloc_callback([function](std::span<Value> args, Value self) -> Value {
121
122
  Parser<ArgSpec...> parser{args, self};
122
- using Result = decltype(parser.parse(detail::unsafe_ruby(), std::move(function)));
123
+ using Result = decltype(parser.parse(Ruby::unsafe_get(), std::move(function)));
123
124
  if constexpr(std::is_void_v<Result>) {
124
- parser.parse(detail::unsafe_ruby(), std::move(function));
125
+ parser.parse(Ruby::unsafe_get(), std::move(function));
125
126
  return {};
126
127
  } else {
127
- return into_Value<Result>(parser.parse(detail::unsafe_ruby(), function));
128
+ return into_Value<Result>(parser.parse(Ruby::unsafe_get(), function));
128
129
  }
129
130
  });
130
131
  }
@@ -955,7 +956,7 @@ namespace rcx {
955
956
  return std::string_view(from_Value<String>(value));
956
957
  }
957
958
 
958
- /// Proc
959
+ // Proc
959
960
 
960
961
  namespace value {
961
962
  inline bool Proc::is_lambda() const {
@@ -980,7 +981,7 @@ namespace rcx {
980
981
  };
981
982
  }
982
983
 
983
- /// Exception
984
+ // Exception
984
985
 
985
986
  namespace value {
986
987
  template <std::derived_from<Exception> E, typename... Args>
@@ -988,6 +989,10 @@ namespace rcx {
988
989
  auto const msg = std::vformat(fmt.get(), std::make_format_args(args...));
989
990
  return cls.new_instance(String::intern_from(msg));
990
991
  }
992
+
993
+ inline Exception Exception::new_from_errno(char const *RCX_Nullable message, int err) {
994
+ return detail::unsafe_coerce<Exception>(rb_syserr_new(err, message));
995
+ }
991
996
  }
992
997
 
993
998
  namespace convert {
@@ -1000,6 +1005,40 @@ namespace rcx {
1000
1005
  }
1001
1006
  }
1002
1007
 
1008
+ // IO
1009
+
1010
+ namespace value {
1011
+ inline int IO::descriptor() const {
1012
+ return detail::protect([this]() noexcept { return rb_io_descriptor(as_VALUE()); });
1013
+ }
1014
+
1015
+ inline void IO::check_readable() const {
1016
+ return detail::protect([this]() noexcept {
1017
+ rb_io_t *pio;
1018
+ RB_IO_POINTER(as_VALUE(), pio);
1019
+ rb_io_check_readable(pio);
1020
+ });
1021
+ }
1022
+
1023
+ inline void IO::check_writable() const {
1024
+ return detail::protect([this]() noexcept {
1025
+ rb_io_t *pio;
1026
+ RB_IO_POINTER(as_VALUE(), pio);
1027
+ rb_io_check_writable(pio);
1028
+ });
1029
+ }
1030
+ }
1031
+
1032
+ namespace convert {
1033
+ inline IO FromValue<IO>::convert(Value value) {
1034
+ if(!value.is_kind_of(builtin::IO)) {
1035
+ throw Exception::format(
1036
+ builtin::TypeError, "Expected an IO but got a {}", value.get_class());
1037
+ }
1038
+ return detail::unsafe_coerce<IO>(value.as_VALUE());
1039
+ }
1040
+ }
1041
+
1003
1042
  #ifdef RCX_IO_BUFFER
1004
1043
  /// IOBuffer
1005
1044
  namespace value {
@@ -1319,6 +1358,18 @@ namespace rcx {
1319
1358
  return define_class<T>(std::forward<decltype(name)>(name), builtin::Object);
1320
1359
  }
1321
1360
 
1361
+ inline Ruby &Ruby::unsafe_get() {
1362
+ static Ruby ruby = {};
1363
+ return ruby;
1364
+ }
1365
+
1366
+ inline Ruby &Ruby::get() {
1367
+ #ifdef HAVE_RUBY_THREAD_HAS_GVL_P
1368
+ rcx_assert(::ruby_thread_has_gvl_p() && "GVL must be held when calling Ruby APIs");
1369
+ #endif
1370
+ return unsafe_get();
1371
+ }
1372
+
1322
1373
  namespace detail {
1323
1374
  inline std::string demangle_type_info(std::type_info const &ti) {
1324
1375
  if constexpr(have_abi_cxa_demangle) {
data/lib/rcx/mkmf.rb CHANGED
@@ -52,3 +52,5 @@ if checking_for("nullability extension") {
52
52
  }
53
53
  $defs.push("-DHAVE_FEATURE_NULLABILITY=1")
54
54
  end
55
+
56
+ have_func('ruby_thread_has_gvl_p', 'ruby/thread.h')
data/lib/rcx/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module RCX
2
- VERSION = -'0.1.0'
2
+ VERSION = -'0.2.0'
3
3
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rcx
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kasumi Hanazuki
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 2025-02-05 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: shellwords
@@ -68,7 +68,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
68
68
  - !ruby/object:Gem::Version
69
69
  version: '0'
70
70
  requirements: []
71
- rubygems_version: 3.7.0.dev
71
+ rubygems_version: 3.6.7
72
72
  specification_version: 4
73
73
  summary: Write Ruby extension in modern C++
74
74
  test_files: []