From 40f14ff2b1f84046f00bb97c16ffdaa2bb65f696 Mon Sep 17 00:00:00 2001 From: Gabe Black Date: Tue, 13 Oct 2020 03:46:00 -0700 Subject: [PATCH] python: Make standard Params::create() optional. The *vast* majority of SimObjects use the standard boilerplate version of their Params::create() method which just returns new ClassName(*this); Rather than force every class to define this method, or annoy and frustrate users who forget and then get linker errors, this change automates the default while leaving the possibility of defining a custom create() method for non-default cases. The situations this mechanism handles can be first broken down by whether the SimObject class has a constructor of the normal form, ie one that takes a const Params reference as its only parameter. If no, then no default create() implementation is defined, and one *must* be defined by the user. If yes, then a default create() implementation is defined as a weak symbol. If the user still wants to define their own create method for some reason, perhaps to add debugging info, to keep track of instances in c++, etc., then they can and it will override the weak symbol and take precedence. The way this is implemented is not straightforward. A set of classes are defined which use SFINAE which either map in the real Params type or a dummy based on whether the normal constructor exists in the SimObject class. Then those classes are used to define *a* create method. Depending on how the SFINAE works out, that will either be *the* create method on the real Params struct, or a create method on a dummy class set up to just absorb the definition and then go away. In either case the create() method is a weak symbol, but in the dummy case it doesn't/shouldn't matter. Annoyingly the compiler insists that the weak symbol be visible. While that makes total sense normally, we don't actually care what happens to the weak symbol if it's attached to the dummy class. Unfortunately that means we need to make the dummy class globally visible, although we put it in a namespace to keep it from colliding with anything useful. Change-Id: I3767a8dc8dc03665a72d5e8c294550d96466f741 Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/35942 Reviewed-by: Gabe Black Reviewed-by: Richard Cooper Maintainer: Gabe Black Tested-by: kokoro --- src/python/m5/SimObject.py | 73 ++++++++++++++++++++++++++++++++++++++ src/sim/sim_object.hh | 29 +++++++++++++++ 2 files changed, 102 insertions(+) diff --git a/src/python/m5/SimObject.py b/src/python/m5/SimObject.py index 458942103..f8d6c27e6 100644 --- a/src/python/m5/SimObject.py +++ b/src/python/m5/SimObject.py @@ -720,6 +720,9 @@ class MetaSimObject(type): code('''#include "pybind11/pybind11.h" #include "pybind11/stl.h" +#include + +#include "base/compiler.hh" #include "params/$cls.hh" #include "python/pybind11/core.hh" #include "sim/init.hh" @@ -801,6 +804,76 @@ module_init(py::module &m_internal) code() code('static EmbeddedPyBind embed_obj("${0}", module_init, "${1}");', cls, cls._base.type if cls._base else "") + if not hasattr(cls, 'abstract') or not cls.abstract: + if 'type' in cls.__dict__: + code() + # This namespace can't *actually* be anonymous, or the compiler + # gets upset about having a weak symbol init. + code('namespace anonymous_params') + code('{') + code() + # If we can't define a default create() method for this params + # struct because the SimObject doesn't have the right + # constructor, use template magic to make it so we're actually + # defining a create method for this class instead. + code('class Dummy${cls}ParamsClass') + code('{') + code(' public:') + code(' ${{cls.cxx_class}} *create() const;') + code('};') + code() + code('template ') + code('class DummyShunt;') + code() + # This version directs to the real Params struct and the + # default behavior of create if there's an appropriate + # constructor. + code('template ') + code('class DummyShunt::value>>') + code('{') + code(' public:') + code(' using Params = ${cls}Params;') + code(' static ${{cls.cxx_class}} *') + code(' create(const Params &p)') + code(' {') + code(' return new CxxClass(p);') + code(' }') + code('};') + code() + # This version diverts to the DummyParamsClass and a dummy + # implementation of create if the appropriate constructor does + # not exist. + code('template ') + code('class DummyShunt::value>>') + code('{') + code(' public:') + code(' using Params = Dummy${cls}ParamsClass;') + code(' static ${{cls.cxx_class}} *') + code(' create(const Params &p)') + code(' {') + code(' return nullptr;') + code(' }') + code('};') + code() + code('} // namespace anonymous_params') + code() + code('using namespace anonymous_params;') + code() + # A weak implementation of either the real Params struct's + # create method, or the Dummy one if we don't want to have + # any default implementation. Either an implementation is + # mandantory since this was shunted off to the dummy class, or + # one is optional which will override this weak version. + code('M5_WEAK ${{cls.cxx_class}} *') + code('DummyShunt<${{cls.cxx_class}}>::Params::create() const') + code('{') + code(' return DummyShunt<${{cls.cxx_class}}>::') + code(' create(*this);') + code('}') _warned_about_nested_templates = False diff --git a/src/sim/sim_object.hh b/src/sim/sim_object.hh index ca2e1d5c3..a75f8dde7 100644 --- a/src/sim/sim_object.hh +++ b/src/sim/sim_object.hh @@ -88,6 +88,35 @@ class ProbeManager; * depth-first traversal is performed (see descendants() in * SimObject.py). This has the effect of calling the method on the * parent node before its children. + * + * The python version of a SimObject class actually represents its Params + * structure which holds all its parameter settings and its name. When python + * needs to create a C++ instance of one of those classes, it uses the Params + * struct's create() method which returns one instance, set up with the + * parameters in the struct. + * + * When writing a SimObject class, there are three different cases as far as + * what you need to do to support the create() method, for hypothetical class + * Foo. + * + * If you have a constructor with a signature like this: + * + * Foo(const FooParams &) + * + * you don't have to do anything, a create method will be automatically + * defined which will call your constructor and return that instance. You + * should use this option most of the time. + * + * If you have a constructor with that signature but still want to define + * your own create method for some reason, you can do that by providing an + * alternative implementation which will override the default. It should have + * this signature: + * + * Foo *FooParams::create() const; + * + * If you don't have a constructor with that signature at all, then you must + * implement the create method with that signature which will build your + * object in some other way. */ class SimObject : public EventManager, public Serializable, public Drainable, public Stats::Group -- 2.30.2