@@ -581,3 +581,94 @@ Here is that function implemented in C:
581
581
Py_DECREF(must_log);
582
582
return ret;
583
583
}
584
+
585
+ ^^^^^^^^^^^^^^^^^^^^^^^^
586
+ Simplifying C++11 class
587
+ ^^^^^^^^^^^^^^^^^^^^^^^^
588
+
589
+ With C++ we can make this a bit smoother. We declare a class thus:
590
+
591
+ .. code-block :: cpp
592
+
593
+ /** Class to simplify default arguments.
594
+ *
595
+ * Usage:
596
+ *
597
+ * static DefaultArg arg_0(PyLong_FromLong(1L));
598
+ * static DefaultArg arg_1(PyUnicode_FromString("Default string."));
599
+ * if (! arg_0 || ! arg_1) {
600
+ * return NULL;
601
+ * }
602
+ *
603
+ * if (! PyArg_ParseTupleAndKeywords(args, kwargs, "...",
604
+ const_cast<char**>(kwlist),
605
+ &arg_0, &arg_1, ...)) {
606
+ return NULL;
607
+ }
608
+ *
609
+ * Then just use arg_0, arg_1 as if they were a PyObject* (possibly
610
+ * might need to be cast to some specific PyObject*).
611
+ *
612
+ * WARN: This class is designed to be statically allocated. If allocated
613
+ * on the heap or stack it will leak memory. That could be fixed by
614
+ * implementing:
615
+ *
616
+ * ~DefaultArg() { Py_XDECREF(m_default); }
617
+ *
618
+ * But this will be highly dangerous when statically allocated as the
619
+ * destructor will be invoked with the Python interpreter in an
620
+ * uncertain state and will, most likely, segfault:
621
+ * "Python(39158,0x7fff78b66310) malloc: *** error for object 0x100511300: pointer being freed was not allocated"
622
+ */
623
+ class DefaultArg {
624
+ public:
625
+ DefaultArg(PyObject *new_ref) : m_arg { NULL }, m_default { new_ref } {}
626
+ // Allow setting of the (optional) argument with
627
+ // PyArg_ParseTupleAndKeywords
628
+ PyObject **operator&() { m_arg = NULL; return &m_arg; }
629
+ // Access the argument or the default if default.
630
+ operator PyObject*() const {
631
+ return m_arg ? m_arg : m_default;
632
+ }
633
+ // Test if constructed successfully from the new reference.
634
+ explicit operator bool() { return m_default != NULL; }
635
+ protected:
636
+ PyObject *m_arg;
637
+ PyObject *m_default;
638
+ };
639
+
640
+
641
+ And we can use ``DefaultArg `` like this:
642
+
643
+ .. code-block :: c
644
+
645
+ static PyObject*
646
+ do_something(something *self, PyObject *args, PyObject *kwds) {
647
+ PyObject *ret = NULL;
648
+ /* Initialise default arguments. */
649
+ static DefaultArg encoding { PyUnicode_FromString("utf-8") };
650
+ static DefaultArg the_id { PyLong_FromLong(0L) };
651
+ static DefaultArg must_log { PyBool_FromLong(1L) };
652
+
653
+ /* Check that the defaults are non-NULL i.e. succesful. */
654
+ if (!encoding || !the_id || !must_log) {
655
+ return NULL;
656
+ }
657
+
658
+ static const char *kwlist[] = { "encoding", "the_id", "must_log", NULL };
659
+ /* &encoding etc. accesses &m_arg in DefaultArg because of PyObject **operator&() */
660
+ if (! PyArg_ParseTupleAndKeywords(args, kwds, "|Oip",
661
+ const_cast<char**>(kwlist),
662
+ &encoding, &the_id, &must_log)) {
663
+ return NULL;
664
+ }
665
+ /*
666
+ * Use encoding, the_id, must_log from here on as PyObject* since we have
667
+ * operator PyObject*() const ...
668
+ *
669
+ * So if we have a function:
670
+ * set_encoding(PyObject *obj) { ... }
671
+ */
672
+ set_encoding(encoding);
673
+ /* ... */
674
+ }
0 commit comments