mirror of https://github.com/KLayout/klayout.git
Merge pull request #632 from KLayout/gsi-enhancements
Enumerators for Ruby, "void to self" for Ruby+Python
This commit is contained in:
commit
8f35c9b486
|
|
@ -284,7 +284,7 @@ void LayoutToNetlist::connect_impl (const db::ShapeCollection &a, const db::Shap
|
|||
m_conn.connect (dla.layer (), dlb.layer ());
|
||||
}
|
||||
|
||||
void LayoutToNetlist::connect_global_impl (const db::ShapeCollection &l, const std::string &gn)
|
||||
size_t LayoutToNetlist::connect_global_impl (const db::ShapeCollection &l, const std::string &gn)
|
||||
{
|
||||
if (m_netlist_extracted) {
|
||||
throw tl::Exception (tl::to_string (tr ("The netlist has already been extracted")));
|
||||
|
|
@ -297,7 +297,7 @@ void LayoutToNetlist::connect_global_impl (const db::ShapeCollection &l, const s
|
|||
db::DeepLayer dl = deep_layer_of (l);
|
||||
m_dlrefs.insert (dl);
|
||||
|
||||
m_conn.connect_global (dl.layer (), gn);
|
||||
return m_conn.connect_global (dl.layer (), gn);
|
||||
}
|
||||
|
||||
const std::string &LayoutToNetlist::global_net_name (size_t id) const
|
||||
|
|
|
|||
|
|
@ -398,18 +398,18 @@ public:
|
|||
* @brief Connects the given layer with a global net with the given name
|
||||
* Returns the global net ID
|
||||
*/
|
||||
void connect_global (const db::Region &l, const std::string &gn)
|
||||
size_t connect_global (const db::Region &l, const std::string &gn)
|
||||
{
|
||||
connect_global_impl (l, gn);
|
||||
return connect_global_impl (l, gn);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Connects the given text layer with a global net with the given name
|
||||
* Returns the global net ID
|
||||
*/
|
||||
void connect_global (const db::Texts &l, const std::string &gn)
|
||||
size_t connect_global (const db::Texts &l, const std::string &gn)
|
||||
{
|
||||
connect_global_impl (l, gn);
|
||||
return connect_global_impl (l, gn);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -872,7 +872,7 @@ private:
|
|||
db::properties_id_type make_netname_propid (db::Layout &ly, const tl::Variant &netname_prop, const db::Net &net) const;
|
||||
db::CellMapping make_cell_mapping_into (db::Layout &layout, db::Cell &cell, const std::vector<const db::Net *> *nets, bool with_device_cells);
|
||||
void connect_impl (const db::ShapeCollection &a, const db::ShapeCollection &b);
|
||||
void connect_global_impl (const db::ShapeCollection &l, const std::string &gn);
|
||||
size_t connect_global_impl (const db::ShapeCollection &l, const std::string &gn);
|
||||
|
||||
// implementation of NetlistManipulationCallbacks
|
||||
virtual size_t link_net_to_parent_circuit (const Net *subcircuit_net, Circuit *parent_circuit, const DCplxTrans &trans);
|
||||
|
|
|
|||
|
|
@ -357,12 +357,12 @@ Class<db::LayoutToNetlist> decl_dbLayoutToNetlist ("db", "LayoutToNetlist",
|
|||
"\n"
|
||||
"This variant has been introduced in version 0.27.\n"
|
||||
) +
|
||||
gsi::method ("connect_global", (void (db::LayoutToNetlist::*) (const db::Region &, const std::string &)) &db::LayoutToNetlist::connect_global, gsi::arg ("l"), gsi::arg ("global_net_name"),
|
||||
gsi::method ("connect_global", (size_t (db::LayoutToNetlist::*) (const db::Region &, const std::string &)) &db::LayoutToNetlist::connect_global, gsi::arg ("l"), gsi::arg ("global_net_name"),
|
||||
"@brief Defines a connection of the given layer with a global net.\n"
|
||||
"This method returns the ID of the global net. Use \\global_net_name to get "
|
||||
"the name back from the ID."
|
||||
) +
|
||||
gsi::method ("connect_global", (void (db::LayoutToNetlist::*) (const db::Texts &, const std::string &)) &db::LayoutToNetlist::connect_global, gsi::arg ("l"), gsi::arg ("global_net_name"),
|
||||
gsi::method ("connect_global", (size_t (db::LayoutToNetlist::*) (const db::Texts &, const std::string &)) &db::LayoutToNetlist::connect_global, gsi::arg ("l"), gsi::arg ("global_net_name"),
|
||||
"@brief Defines a connection of the given text layer with a global net.\n"
|
||||
"This method returns the ID of the global net. Use \\global_net_name to get "
|
||||
"the name back from the ID."
|
||||
|
|
|
|||
|
|
@ -278,6 +278,29 @@ event._manage
|
|||
and pointers/references and also supports "out" parameters.
|
||||
</p>
|
||||
|
||||
<h3>"void" as return value</h3>
|
||||
|
||||
<p>
|
||||
While a "void" return value indicates "no return value" in C++, this concept is not
|
||||
common in Ruby. Ruby methods will always return the last value generated in
|
||||
a method.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
For consistency, KLayout's Ruby binding returns "self" from methods in this
|
||||
case. This allows chaining of methods in many cases and fosters compact code:
|
||||
</p>
|
||||
|
||||
<pre>// C++
|
||||
class A {
|
||||
public:
|
||||
virtual void f() { ... }
|
||||
virtual void g() { ... }
|
||||
};
|
||||
|
||||
# Ruby
|
||||
a = A::new.f.g</pre>
|
||||
|
||||
<h3>References and pointers to simple types (FixNum, Float, String)</h3>
|
||||
|
||||
<p>
|
||||
|
|
@ -499,6 +522,16 @@ public:
|
|||
a = A::new
|
||||
a.each { |i| ... }</pre>
|
||||
|
||||
<p>
|
||||
If no block is given, an Enumerator object is created. Enumerators
|
||||
are a Ruby feature. Enumerators support many convenient methods
|
||||
like sort, inject, collect, select etc. Here is an example:
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
# turns all elements returned by the iterator into strings and sorts them
|
||||
sorted = a.each.collect(&:to_s).sort</pre>
|
||||
|
||||
<p>
|
||||
Iterators match very well between C++ and Ruby so there are no real issues here.
|
||||
The return type of the iterators is mapped to Ruby's block arguments using the
|
||||
|
|
|
|||
|
|
@ -686,6 +686,15 @@ property_name_from_id (int mid, PyObject *self)
|
|||
return cls_decl->name () + "." + mt->property_name (mid);
|
||||
}
|
||||
|
||||
static gsi::ArgType create_void_type ()
|
||||
{
|
||||
gsi::ArgType at;
|
||||
at.init<void> ();
|
||||
return at;
|
||||
}
|
||||
|
||||
static gsi::ArgType s_void_type = create_void_type ();
|
||||
|
||||
static PyObject *
|
||||
get_return_value (PYAObjectBase *self, gsi::SerialArgs &retlist, const gsi::MethodBase *meth, tl::Heap &heap)
|
||||
{
|
||||
|
|
@ -696,6 +705,12 @@ get_return_value (PYAObjectBase *self, gsi::SerialArgs &retlist, const gsi::Meth
|
|||
gsi::IterAdaptorAbstractBase *iter = (gsi::IterAdaptorAbstractBase *) retlist.read<gsi::IterAdaptorAbstractBase *> (heap);
|
||||
ret = (PyObject *) PYAIteratorObject::create (self ? self->py_object () : 0, iter, &meth->ret_type ());
|
||||
|
||||
} else if (meth->ret_type () == s_void_type && self != 0) {
|
||||
|
||||
// simple, yet magical :)
|
||||
ret = self->py_object ();
|
||||
Py_INCREF (ret);
|
||||
|
||||
} else {
|
||||
|
||||
ret = pop_arg (meth->ret_type (), retlist, self, heap).release ();
|
||||
|
|
|
|||
|
|
@ -89,7 +89,8 @@ public:
|
|||
{
|
||||
std::vector<tl::BacktraceElement> bt;
|
||||
bt.push_back (tl::BacktraceElement (rb_sourcefile (), rb_sourceline ()));
|
||||
rba_get_backtrace_from_array (rb_funcall (rb_mKernel, rb_intern ("caller"), 0), bt, 0);
|
||||
static ID id_caller = rb_intern ("caller");
|
||||
rba_get_backtrace_from_array (rb_funcall (rb_mKernel, id_caller, 0), bt, 0);
|
||||
return bt;
|
||||
}
|
||||
|
||||
|
|
@ -113,7 +114,8 @@ public:
|
|||
// But the purpose is a relative compare, so efficiency is not sacrificed here
|
||||
// for unnecessary consistency.
|
||||
int d = 1;
|
||||
VALUE backtrace = rb_funcall (rb_mKernel, rb_intern ("caller"), 0);
|
||||
static ID id_caller = rb_intern ("caller");
|
||||
VALUE backtrace = rb_funcall (rb_mKernel, id_caller, 0);
|
||||
if (TYPE (backtrace) == T_ARRAY) {
|
||||
d += RARRAY_LEN(backtrace);
|
||||
}
|
||||
|
|
@ -885,6 +887,15 @@ method_name_from_id (int mid, VALUE self)
|
|||
return cls_decl->name () + "::" + mt->name (mid);
|
||||
}
|
||||
|
||||
static gsi::ArgType create_void_type ()
|
||||
{
|
||||
gsi::ArgType at;
|
||||
at.init<void> ();
|
||||
return at;
|
||||
}
|
||||
|
||||
static gsi::ArgType s_void_type = create_void_type ();
|
||||
|
||||
VALUE
|
||||
method_adaptor (int mid, int argc, VALUE *argv, VALUE self, bool ctor)
|
||||
{
|
||||
|
|
@ -933,16 +944,18 @@ method_adaptor (int mid, int argc, VALUE *argv, VALUE self, bool ctor)
|
|||
|
||||
if (p) {
|
||||
|
||||
static ID id_set = rb_intern ("set");
|
||||
|
||||
VALUE signal_handler = p->signal_handler (meth);
|
||||
|
||||
if (rb_block_given_p ()) {
|
||||
|
||||
VALUE proc = rb_block_proc ();
|
||||
RB_GC_GUARD (proc);
|
||||
ret = rba_funcall2_checked (signal_handler, rb_intern ("set"), 1, &proc);
|
||||
ret = rba_funcall2_checked (signal_handler, id_set, 1, &proc);
|
||||
|
||||
} else if (argc > 0) {
|
||||
ret = rba_funcall2_checked (signal_handler, rb_intern ("set"), argc, argv);
|
||||
ret = rba_funcall2_checked (signal_handler, id_set, argc, argv);
|
||||
} else {
|
||||
ret = signal_handler;
|
||||
}
|
||||
|
|
@ -991,6 +1004,24 @@ method_adaptor (int mid, int argc, VALUE *argv, VALUE self, bool ctor)
|
|||
p->set (obj, true, false, true, self);
|
||||
}
|
||||
|
||||
} else if (meth->ret_type ().is_iter () && ! rb_block_given_p ()) {
|
||||
|
||||
// calling an iterator method without block -> deliver an enumerator using "to_enum"
|
||||
|
||||
static ID id_to_enum = rb_intern ("to_enum");
|
||||
|
||||
VALUE method_sym = ID2SYM (rb_intern (meth->primary_name ().c_str ()));
|
||||
|
||||
if (argc == 0) {
|
||||
ret = rba_funcall2_checked (self, id_to_enum, 1, &method_sym);
|
||||
} else {
|
||||
std::vector<VALUE> new_args;
|
||||
new_args.reserve (size_t (argc + 1));
|
||||
new_args.push_back (method_sym);
|
||||
new_args.insert (new_args.end (), argv, argv + argc);
|
||||
ret = rba_funcall2_checked (self, id_to_enum, argc + 1, new_args.begin ().operator-> ());
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
void *obj = 0;
|
||||
|
|
@ -1055,8 +1086,15 @@ method_adaptor (int mid, int argc, VALUE *argv, VALUE self, bool ctor)
|
|||
|
||||
}
|
||||
|
||||
} else if (meth->ret_type () == s_void_type) {
|
||||
|
||||
// simple, yet magical :)
|
||||
return self;
|
||||
|
||||
} else {
|
||||
|
||||
ret = pop_arg (meth->ret_type (), p, retlist, heap);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -728,6 +728,17 @@ class DBPolygonTests(unittest.TestCase):
|
|||
self.assertEqual(str(p1), "(21,42;21,62;41,62;41,42)")
|
||||
self.assertEqual(str(pp), "(21,42;21,62;41,62;41,42)")
|
||||
|
||||
def test_voidMethodsReturnSelf(self):
|
||||
|
||||
hull = [ pya.Point(0, 0), pya.Point(6000, 0),
|
||||
pya.Point(6000, 3000), pya.Point(0, 3000) ]
|
||||
hole1 = [ pya.Point(1000, 1000), pya.Point(2000, 1000),
|
||||
pya.Point(2000, 2000), pya.Point(1000, 2000) ]
|
||||
hole2 = [ pya.Point(3000, 1000), pya.Point(4000, 1000),
|
||||
pya.Point(4000, 2000), pya.Point(3000, 2000) ]
|
||||
poly = pya.Polygon(hull).insert_hole(hole1).insert_hole(hole2)
|
||||
self.assertEqual(str(poly), "(0,0;0,3000;6000,3000;6000,0/1000,1000;2000,1000;2000,2000;1000,2000/3000,1000;4000,1000;4000,2000;3000,2000)")
|
||||
|
||||
# run unit tests
|
||||
if __name__ == '__main__':
|
||||
suite = unittest.TestLoader().loadTestsFromTestCase(DBPolygonTests)
|
||||
|
|
|
|||
|
|
@ -733,6 +733,8 @@ class Basic_TestClass < TestBase
|
|||
arr = []
|
||||
b.each_b_copy { |bb| arr.push(bb.b2) }
|
||||
assert_equal(arr, ["a", "y", "uu"])
|
||||
# through enumerator
|
||||
assert_equal(b.each_b_copy.collect(&:b2), ["a", "y", "uu"])
|
||||
|
||||
arr = []
|
||||
b.each_b_copy { |bb| bb.b5(bb.b2 + "x"); arr.push(bb.b2) }
|
||||
|
|
@ -745,6 +747,8 @@ class Basic_TestClass < TestBase
|
|||
arr = []
|
||||
b.each_b_cref { |bb| arr.push(bb.b2) }
|
||||
assert_equal(arr, ["a", "y", "uu"])
|
||||
# through enumerator
|
||||
assert_equal(b.each_b_cref.collect(&:b2), ["a", "y", "uu"])
|
||||
|
||||
arr = []
|
||||
# this works, since the "const B &" will be converted to a copy
|
||||
|
|
@ -759,6 +763,8 @@ class Basic_TestClass < TestBase
|
|||
arr = []
|
||||
b.each_b_cptr { |bb| arr.push(bb.b2) }
|
||||
assert_equal(arr, ["a", "y", "uu"])
|
||||
# through enumerator
|
||||
assert_equal(b.each_b_cptr.collect(&:b2), ["a", "y", "uu"])
|
||||
|
||||
arr = []
|
||||
# const references cannot be modified
|
||||
|
|
@ -778,6 +784,8 @@ class Basic_TestClass < TestBase
|
|||
arr = []
|
||||
b.each_b_ref { |bb| arr.push(bb.b2) }
|
||||
assert_equal(arr, ["a", "y", "uu"])
|
||||
# through enumerator
|
||||
assert_equal(b.each_b_ref.collect(&:b2), ["a", "y", "uu"])
|
||||
|
||||
arr = []
|
||||
b.each_b_ref { |bb| bb.b5(bb.b2 + "x"); arr.push(bb.b2) }
|
||||
|
|
@ -790,6 +798,8 @@ class Basic_TestClass < TestBase
|
|||
arr = []
|
||||
b.each_b_ptr { |bb| arr.push(bb.b2) }
|
||||
assert_equal(arr, ["ax", "yx", "uux"])
|
||||
# through enumerator
|
||||
assert_equal(b.each_b_ptr.collect(&:b2), ["ax", "yx", "uux"])
|
||||
|
||||
arr = []
|
||||
b.each_b_ptr { |bb| bb.b5(bb.b2 + "x"); arr.push(bb.b2) }
|
||||
|
|
|
|||
|
|
@ -67,6 +67,8 @@ class DBPolygon_TestClass < TestBase
|
|||
arr = []
|
||||
a.each_point_hull { |p| arr.push( p.to_s ) }
|
||||
assert_equal( arr, ["5,-10", "5,15", "20,15", "20,-10"] )
|
||||
# with enumerator
|
||||
assert_equal( a.each_point_hull.collect(&:to_s), ["5,-10", "5,15", "20,15", "20,-10"] )
|
||||
|
||||
b = a.dup
|
||||
|
||||
|
|
@ -130,6 +132,8 @@ class DBPolygon_TestClass < TestBase
|
|||
arr = []
|
||||
a.each_point_hole(0) { |p| arr.push( p.to_s ) }
|
||||
assert_equal( arr, ["1,2", "2,2", "2,6"] )
|
||||
# with enumerator
|
||||
assert_equal( a.each_point_hole(0).collect(&:to_s), ["1,2", "2,2", "2,6"] )
|
||||
|
||||
arr = []
|
||||
a.each_edge { |p| arr.push( p.to_s ) }
|
||||
|
|
@ -773,6 +777,19 @@ class DBPolygon_TestClass < TestBase
|
|||
|
||||
end
|
||||
|
||||
def test_voidMethodsReturnSelf
|
||||
|
||||
hull = [ RBA::Point::new(0, 0), RBA::Point::new(6000, 0),
|
||||
RBA::Point::new(6000, 3000), RBA::Point::new(0, 3000) ]
|
||||
hole1 = [ RBA::Point::new(1000, 1000), RBA::Point::new(2000, 1000),
|
||||
RBA::Point::new(2000, 2000), RBA::Point::new(1000, 2000) ]
|
||||
hole2 = [ RBA::Point::new(3000, 1000), RBA::Point::new(4000, 1000),
|
||||
RBA::Point::new(4000, 2000), RBA::Point::new(3000, 2000) ]
|
||||
poly = RBA::Polygon::new(hull).insert_hole(hole1).insert_hole(hole2)
|
||||
assert_equal(poly.to_s, "(0,0;0,3000;6000,3000;6000,0/1000,1000;2000,1000;2000,2000;1000,2000/3000,1000;4000,1000;4000,2000;3000,2000)")
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
load("test_epilogue.rb")
|
||||
|
|
|
|||
Loading…
Reference in New Issue