Class Coercion in Ruby C extensions25 Jul 2015 GSoC-2015 · SymEngine · Ruby
Hola Everyone! Our classes started this week, and there was less time during the week than I had expected. Anyways, I am at the completion of the Ruby wrappers for the
Rational classes. It would have been complete last week itself if it weren’t for the deadlines I had for registration. Mostly because of the companies that will be coming to the campus this week to select interns. This time I really am going to complete it this weekend for sure.
I didn’t know of this technique until Isuru suggested me not to modify the existing Ruby classes, and rather have a look at Class Coercion in Ruby. This is an elegant concept to get seamless inter-operation between existing classes and new classes without having to modify the default library code.
My aim was to support operation of SymEngine classes like
Complex with the existing classes
Rational classes from the Ruby kernel. For that I had supported the operations in the C wrappers. Like, if the other parameter object passed to the method is not of a SymEngine type but a Ruby object, I would first convert it into the corresponding SymEngine object, and then use it for operation.
This supported all the operations of the kind
a * 2, where
a is an object of the type
SymEngine::Integer. But I had to support
2 * a too. That meant (for me) changing the existing class. Overriding all the existing binary operations for it to support
SymEngine types, violating the open/closed principle. It was like this
class Fixnum alias_method :old_add, :+ def +(other) if other.is_a? SymEngine::Basic SymEngine::Integer.new(self) + other else old_add other end end alias_method :old_sub, :- def -(other) if other.is_a? SymEngine::Basic SymEngine::Integer.new(self) - other else old_sub other end end alias_method :old_mul, :* def *(other) if other.is_a? SymEngine::Basic SymEngine::Integer.new(self) * other else old_mul other end end alias_method :old_div, :/ def /(other) if other.is_a? SymEngine::Basic SymEngine::Integer.new(self) / other else old_div other end end alias_method :old_pow, :** def **(other) if other.is_a? SymEngine::Basic SymEngine::Integer.new(self) ** other else old_pow other end end end
And to make it more WET, I had to do the same for the
I had to use aliases to avoid getting in a endless recursive loop, that eventually lead to stack overflow.
The blog post explains the procedure of using Class Coercion in detail. Please give it a read. I would also have updated this post to include my implementation in the
By this time next week, I would have fully supported the
Complex class and it’s methods. The RSpec counter-part of the
test_arit.py would have been done too.
- Configured spec_helper to work with test_unit
- Added test-unit as development dependency
- Added script to test with valgrind
- Added script to test with callgrind tool
- Updated .gitignore Added notebooks/Gemfile.lock and callgrind generated files
- Added source for
- Added tests for
- Made a macro to get Integer from T_INTEGER VALUE
- Added source for
.newmethod in Rational
- Added tests for instantiation of Rational
- [ci skip] Added script to debug tests in gdb
- Integer class now supports
comments powered by Disqus