Introducing SPICE profile references

This commit is contained in:
Matthias Koefferlein 2026-07-01 23:43:26 +02:00
parent a9c4808d14
commit 6c4a22d6da
4 changed files with 276 additions and 72 deletions

View File

@ -88,7 +88,9 @@ public:
*/ */
db::DeviceClass *default_device_class () db::DeviceClass *default_device_class ()
{ {
return mp_factory->create_class (); db::DeviceClass *dc = mp_factory->create_class ();
dc->set_name (name ());
return dc;
} }
private: private:

View File

@ -361,7 +361,6 @@ Class<db::DeviceClass::SpiceProfile> decl_dbDeviceClassSpiceProfile ("db", "Devi
"term = sp.terminal_order\n" "term = sp.terminal_order\n"
"term.append('B')\n" "term.append('B')\n"
"sp.terminal_order = term\n" "sp.terminal_order = term\n"
"cls.set_spice_profile(sp)\n"
"@/code\n" "@/code\n"
"\n" "\n"
"For special cases, the terminal order can have optional elements. These optional terminals " "For special cases, the terminal order can have optional elements. These optional terminals "
@ -427,7 +426,6 @@ Class<db::DeviceClass::SpiceProfile> decl_dbDeviceClassSpiceProfile ("db", "Devi
"par = sp.incoming_parameters\n" "par = sp.incoming_parameters\n"
"par['P'] = 'P*2.0'\n" "par['P'] = 'P*2.0'\n"
"sp.incoming_parameters = par\n" "sp.incoming_parameters = par\n"
"cls.set_spice_profile(sp)\n"
"@/code\n" "@/code\n"
) + ) +
gsi::method_ext ("incoming_parameters=", &set_incoming_parameters, gsi::arg ("par"), gsi::method_ext ("incoming_parameters=", &set_incoming_parameters, gsi::arg ("par"),
@ -465,7 +463,6 @@ Class<db::DeviceClass::SpiceProfile> decl_dbDeviceClassSpiceProfile ("db", "Devi
"par = sp.outgoing_parameters\n" "par = sp.outgoing_parameters\n"
"par['P'] = 'P*2.0'\n" "par['P'] = 'P*2.0'\n"
"sp.outgoing_parameters = par\n" "sp.outgoing_parameters = par\n"
"cls.set_spice_profile(sp)\n"
"@/code\n" "@/code\n"
) + ) +
gsi::method_ext ("outgoing_parameters=", &set_outgoing_parameters, gsi::arg ("par"), gsi::method_ext ("outgoing_parameters=", &set_outgoing_parameters, gsi::arg ("par"),
@ -480,6 +477,139 @@ Class<db::DeviceClass::SpiceProfile> decl_dbDeviceClassSpiceProfile ("db", "Devi
"This class has been added in version 0.31.0." "This class has been added in version 0.31.0."
); );
namespace
{
class SpiceProfileReference
{
public:
SpiceProfileReference (db::DeviceClass *dc, const std::string &name, bool c)
: mp_device_class (dc), m_name (name), m_const (c)
{
// .. nothing yet ..
}
const std::string &get_element ()
{
return dc ()->spice_profile (m_name).element;
}
void set_element (const std::string &element)
{
auto sp = get ();
sp.element = element;
check_writable ();
dc ()->set_spice_profile (m_name, sp);
}
const std::vector<std::string> &get_terminal_order ()
{
return dc ()->spice_profile (m_name).terminal_order;
}
void set_terminal_order (const std::vector<std::string> &terminal_order)
{
auto sp = get ();
sp.terminal_order = terminal_order;
check_writable ();
dc ()->set_spice_profile (m_name, sp);
}
const std::map<std::string, std::string> &get_incoming_parameters ()
{
return dc ()->spice_profile (m_name).incoming_parameters;
}
void set_incoming_parameters (const std::map<std::string, std::string> &pd)
{
auto sp = get ();
sp.incoming_parameters = pd;
check_writable ();
dc ()->set_spice_profile (m_name, sp);
}
const std::map<std::string, std::string> &get_outgoing_parameters ()
{
return dc ()->spice_profile (m_name).outgoing_parameters;
}
void set_outgoing_parameters (const std::map<std::string, std::string> &pd)
{
auto sp = get ();
sp.outgoing_parameters = pd;
check_writable ();
dc ()->set_spice_profile (m_name, sp);
}
db::DeviceClass::SpiceProfile get () const
{
return const_cast<SpiceProfileReference *> (this)->dc ()->spice_profile (m_name);
}
private:
db::DeviceClass *dc ()
{
if (! mp_device_class) {
throw tl::Exception (tl::to_string (tr ("DeviceClass object does no longer exists - invalid SPICE profile reference")));
}
return mp_device_class.get ();
}
void check_writable ()
{
if (m_const) {
throw tl::Exception (tl::to_string (tr ("SPICE profile reference is immutable - cannot use a setter on this reference")));
}
}
tl::weak_ptr<db::DeviceClass> mp_device_class;
std::string m_name;
bool m_const;
};
}
Class<SpiceProfileReference> decl_dbDeviceClassSpiceProfileReference ("db", "DeviceClassSpiceProfileReference",
gsi::method ("element", &SpiceProfileReference::get_element,
"@brief Gets the SPICE element to use for this device.\n"
"See \\DeviceClassSpiceProfile#element for a description of this attribute."
) +
gsi::method ("element=", &SpiceProfileReference::set_element, gsi::arg ("e"),
"@brief Sets the SPICE element to use for this device.\n"
"See \\DeviceClassSpiceProfile#element= for a description of this attribute."
) +
gsi::method ("terminal_order", &SpiceProfileReference::get_terminal_order,
"@brief Gets the terminal order to use for this device.\n"
"See \\DeviceClassSpiceProfile#terminal_order for a description of this attribute."
) +
gsi::method ("terminal_order=", &SpiceProfileReference::set_terminal_order, gsi::arg ("to"),
"@brief Sets the terminal order to use for this device.\n"
"See \\DeviceClassSpiceProfile#terminal_order= for a description of this attribute."
) +
gsi::method ("incoming_parameters", &SpiceProfileReference::get_incoming_parameters,
"@brief Gets the mapping of incoming parameters from SPICE files\n"
"See \\DeviceClassSpiceProfile#incoming_parameters for a description of this attribute."
) +
gsi::method ("incoming_parameters=", &SpiceProfileReference::set_incoming_parameters, gsi::arg ("par"),
"@brief Sets the mapping of incoming parameters from SPICE files\n"
"See \\DeviceClassSpiceProfile#incoming_parameters= for a description of this attribute."
) +
gsi::method ("outgoing_parameters", &SpiceProfileReference::get_outgoing_parameters,
"@brief Gets the mapping tables for outgoing parameters\n"
"See \\DeviceClassSpiceProfile#outgoing_parameters for a description of this attribute."
) +
gsi::method ("outgoing_parameters=", &SpiceProfileReference::set_outgoing_parameters, gsi::arg ("par"),
"@brief Sets the mapping tables for outgoing parameters\n"
"See \\DeviceClassSpiceProfile#outgoing_parameters= for a description of this attribute."
),
"@brief Represents a SPICE profile of a device\n"
"This class has the same interface than the SPICE profile object \\DeviceClassSpiceProfile, but\n"
"represents a reference to a SPICE profile inside a \\DeviceClass object. This reference\n"
"allows manipulating SPICE profiles inside DeviceClass objects.\n"
"\n"
"This class has been added in version 0.31.0."
);
static tl::id_type id_of_device_class (const db::DeviceClass *cls) static tl::id_type id_of_device_class (const db::DeviceClass *cls)
{ {
return tl::id_of (cls); return tl::id_of (cls);
@ -554,6 +684,31 @@ static void set_default_spice_profile (db::DeviceClass *cls, const db::DeviceCla
cls->set_spice_profile (std::string (), profile); cls->set_spice_profile (std::string (), profile);
} }
static void set_named_spice_profile (db::DeviceClass *cls, const std::string &name, const db::DeviceClass::SpiceProfile &profile)
{
cls->set_spice_profile (name, profile);
}
static void set_default_spice_profile_ref (db::DeviceClass *cls, const SpiceProfileReference &profile)
{
cls->set_spice_profile (std::string (), profile.get ());
}
static void set_named_spice_profile_ref (db::DeviceClass *cls, const std::string &name, const SpiceProfileReference &profile)
{
cls->set_spice_profile (name, profile.get ());
}
static SpiceProfileReference get_spice_profile (db::DeviceClass *cls, const std::string &name)
{
return SpiceProfileReference (cls, name, false);
}
static SpiceProfileReference get_spice_profile_const (const db::DeviceClass *cls, const std::string &name)
{
return SpiceProfileReference (const_cast<db::DeviceClass *> (cls), name, true);
}
Class<db::DeviceClass> decl_dbDeviceClass ("db", "DeviceClass", Class<db::DeviceClass> decl_dbDeviceClass ("db", "DeviceClass",
gsi::method ("name", &db::DeviceClass::name, gsi::method ("name", &db::DeviceClass::name,
"@brief Gets the name of the device class." "@brief Gets the name of the device class."
@ -584,7 +739,7 @@ Class<db::DeviceClass> decl_dbDeviceClass ("db", "DeviceClass",
"\n" "\n"
"SPICE profiles have been introduced in version 0.31.0." "SPICE profiles have been introduced in version 0.31.0."
) + ) +
gsi::method ("set_spice_profile", &db::DeviceClass::set_spice_profile, gsi::arg ("profile_name"), gsi::arg ("spice_profile"), gsi::method_ext ("set_spice_profile", &set_named_spice_profile, gsi::arg ("profile_name"), gsi::arg ("spice_profile"),
"@brief Defines a SPICE profile.\n" "@brief Defines a SPICE profile.\n"
"SPICE profiles are a way to declare SPICE representations for a specific device. " "SPICE profiles are a way to declare SPICE representations for a specific device. "
"Each device class can support multiple profiles. An empty name declares the default profile, " "Each device class can support multiple profiles. An empty name declares the default profile, "
@ -599,6 +754,13 @@ Class<db::DeviceClass> decl_dbDeviceClass ("db", "DeviceClass",
"\n" "\n"
"SPICE profiles have been introduced in version 0.31.0." "SPICE profiles have been introduced in version 0.31.0."
) + ) +
gsi::method_ext ("set_spice_profile", &set_named_spice_profile_ref, gsi::arg ("profile_name"), gsi::arg ("spice_profile_ref"),
"@brief Defines a SPICE profile from a reference to another profile.\n"
"Like the other versions of that method, this method defines a SPICE profile, but taking a reference to another "
"profile as the source.\n"
"\n"
"SPICE profiles have been introduced in version 0.31.0."
) +
gsi::method_ext ("set_spice_profile", &set_default_spice_profile, gsi::arg ("spice_profile"), gsi::method_ext ("set_spice_profile", &set_default_spice_profile, gsi::arg ("spice_profile"),
"@brief Sets the default SPICE profile.\n" "@brief Sets the default SPICE profile.\n"
"This method is like the other \\set_spice_profile method, but uses an empty string for the profile name. " "This method is like the other \\set_spice_profile method, but uses an empty string for the profile name. "
@ -606,22 +768,38 @@ Class<db::DeviceClass> decl_dbDeviceClass ("db", "DeviceClass",
"\n" "\n"
"SPICE profiles have been introduced in version 0.31.0." "SPICE profiles have been introduced in version 0.31.0."
) + ) +
gsi::method ("spice_profile", &db::DeviceClass::spice_profile, gsi::arg ("profile_name", std::string ()), gsi::method_ext ("set_spice_profile", &set_default_spice_profile_ref, gsi::arg ("spice_profile_ref"),
"@brief Sets the default SPICE profile from a reference to another profile.\n"
"Like the other versions of that method, this method defines a SPICE profile, but taking a reference to another "
"profile as the source.\n"
"\n"
"SPICE profiles have been introduced in version 0.31.0."
) +
gsi::method_ext ("spice_profile", &get_spice_profile, gsi::arg ("profile_name", std::string ()),
"@brief Gets a SPICE profile with the given name.\n" "@brief Gets a SPICE profile with the given name.\n"
"If the name is not corresponding to a valid profile and a fallback profile '*' exists, the latter is returned. " "If the name is not corresponding to a valid profile and a fallback profile '*' exists, the latter is returned. "
"Otherwise, a default profile is returned, which triggers the default serialization.\n" "Otherwise, a default profile is returned, which triggers the default serialization.\n"
"See \\set_spice_profile for a description of the SPICE profile concept.\n" "See \\set_spice_profile for a description of the SPICE profile concept.\n"
"\n" "\n"
"Note that this method returns a copy of the profile object. If you want to manipulate a SPICE profile, " "Note that this method returns a reference to a SPICE profile. You can manipulate the profile by changing attributes:\n"
"you have to fetch the current profile, change it and store it again:\n"
"\n" "\n"
"@code\n" "@code\n"
"dc = ... # some DeviceClass\n" "dc = ... # some DeviceClass\n"
"profile = dc.spice_profile(\"\") # default profile\n" "profile = dc.spice_profile(\"\") # default profile\n"
"profile.element = \"X\"\n" "profile.element = \"X\"\n"
"dc.set_spice_profile(\"\", profile)\n"
"@/code\n" "@/code\n"
"\n" "\n"
"Changing a SPICE profile may implicitly create new profiles in the device class, for example, if originally "
"the profile was obtained from the fallback profile. After changing the attributes, a copy of the profile will "
"be created in the device class, with the new, specific settings.\n"
"\n"
"SPICE profiles have been introduced in version 0.31.0."
) +
gsi::method_ext ("spice_profile", &get_spice_profile_const, gsi::arg ("profile_name", std::string ()),
"@brief Gets an immutable SPICE profile with the given name.\n"
"Like the other method with the same name, this method returns a SPICE profile reference. When used on a "
"const DeviceClass object, this reference is immutable, i.e. you can't change attributes of the SPICE profile.\n"
"\n"
"SPICE profiles have been introduced in version 0.31.0." "SPICE profiles have been introduced in version 0.31.0."
) + ) +
gsi::method ("strict?", &db::DeviceClass::is_strict, gsi::method ("strict?", &db::DeviceClass::is_strict,

View File

@ -1286,6 +1286,30 @@ END
assert_equal(c.spice_profile("").element, "U") assert_equal(c.spice_profile("").element, "U")
assert_equal(c.spice_profile("PROFILE").element, "Q") assert_equal(c.spice_profile("PROFILE").element, "Q")
# SPICE profile references
assert_equal(c.has_spice_profile("X"), false)
c.spice_profile("X").element = "K"
assert_equal(c.has_spice_profile("X"), true)
assert_equal(c.spice_profile().element, "U")
assert_equal(c.spice_profile("X").element, "K")
c.set_spice_profile("Y", c.spice_profile("X"))
c.spice_profile("X").element = "L"
assert_equal(c.has_spice_profile("Y"), true)
assert_equal(c.spice_profile("X").element, "L")
assert_equal(c.spice_profile("Y").element, "K")
c.spice_profile("X").terminal_order = [ "1", "2" ]
assert_equal(c.spice_profile("X").terminal_order, [ "1", "2" ])
c.spice_profile("X").incoming_parameters = { "P1" => "A", "P2" => "B" }
assert_equal(c.spice_profile("X").incoming_parameters, { "P1" => "A", "P2" => "B" })
c.spice_profile("X").outgoing_parameters = { "A" => "P1", "B" => "P2" }
assert_equal(c.spice_profile("X").outgoing_parameters, { "A" => "P1", "B" => "P2" })
end end
end end

View File

@ -86,103 +86,103 @@ class DBNetlistExtractorTests_TestClass < TestBase
def test_3_Factory def test_3_Factory
ex = RBA::DeviceExtractorMOS3Transistor::new("myclass") ex = RBA::DeviceExtractorMOS3Transistor::new("myclass")
ex.test_initialize(RBA::Netlist::new) dc = ex.default_device_class
assert_equal(ex.device_class.name, "myclass") assert_equal(dc.name, "myclass")
assert_equal(ex.device_class.class == MyClass, false) assert_equal(dc.class == MyClass, false)
assert_equal(ex.device_class.class == RBA::DeviceClassMOS3Transistor, true) assert_equal(dc.class == RBA::DeviceClassMOS3Transistor, true)
ex = RBA::DeviceExtractorMOS3Transistor::new("myclass", false, MyFactory.new) ex = RBA::DeviceExtractorMOS3Transistor::new("myclass", false, MyFactory.new)
ex.test_initialize(RBA::Netlist::new) dc = ex.default_device_class
assert_equal(ex.device_class.name, "myclass") assert_equal(dc.name, "myclass")
assert_equal(ex.device_class.class == MyClass, true) assert_equal(dc.class == MyClass, true)
ex = RBA::DeviceExtractorMOS4Transistor::new("myclass") ex = RBA::DeviceExtractorMOS4Transistor::new("myclass")
ex.test_initialize(RBA::Netlist::new) dc = ex.default_device_class
assert_equal(ex.device_class.name, "myclass") assert_equal(dc.name, "myclass")
assert_equal(ex.device_class.class == MyClass, false) assert_equal(dc.class == MyClass, false)
assert_equal(ex.device_class.class == RBA::DeviceClassMOS4Transistor, true) assert_equal(dc.class == RBA::DeviceClassMOS4Transistor, true)
ex = RBA::DeviceExtractorMOS4Transistor::new("myclass", false, MyFactory.new) ex = RBA::DeviceExtractorMOS4Transistor::new("myclass", false, MyFactory.new)
ex.test_initialize(RBA::Netlist::new) dc = ex.default_device_class
assert_equal(ex.device_class.name, "myclass") assert_equal(dc.name, "myclass")
assert_equal(ex.device_class.class == MyClass, true) assert_equal(dc.class == MyClass, true)
ex = RBA::DeviceExtractorBJT3Transistor::new("myclass") ex = RBA::DeviceExtractorBJT3Transistor::new("myclass")
ex.test_initialize(RBA::Netlist::new) dc = ex.default_device_class
assert_equal(ex.device_class.name, "myclass") assert_equal(dc.name, "myclass")
assert_equal(ex.device_class.class == MyClass, false) assert_equal(dc.class == MyClass, false)
assert_equal(ex.device_class.class == RBA::DeviceClassBJT3Transistor, true) assert_equal(dc.class == RBA::DeviceClassBJT3Transistor, true)
ex = RBA::DeviceExtractorBJT3Transistor::new("myclass", MyFactory.new) ex = RBA::DeviceExtractorBJT3Transistor::new("myclass", MyFactory.new)
ex.test_initialize(RBA::Netlist::new) dc = ex.default_device_class
assert_equal(ex.device_class.name, "myclass") assert_equal(dc.name, "myclass")
assert_equal(ex.device_class.class == MyClass, true) assert_equal(dc.class == MyClass, true)
ex = RBA::DeviceExtractorBJT4Transistor::new("myclass") ex = RBA::DeviceExtractorBJT4Transistor::new("myclass")
ex.test_initialize(RBA::Netlist::new) dc = ex.default_device_class
assert_equal(ex.device_class.name, "myclass") assert_equal(dc.name, "myclass")
assert_equal(ex.device_class.class == MyClass, false) assert_equal(dc.class == MyClass, false)
assert_equal(ex.device_class.class == RBA::DeviceClassBJT4Transistor, true) assert_equal(dc.class == RBA::DeviceClassBJT4Transistor, true)
ex = RBA::DeviceExtractorBJT4Transistor::new("myclass", MyFactory.new) ex = RBA::DeviceExtractorBJT4Transistor::new("myclass", MyFactory.new)
ex.test_initialize(RBA::Netlist::new) dc = ex.default_device_class
assert_equal(ex.device_class.name, "myclass") assert_equal(dc.name, "myclass")
assert_equal(ex.device_class.class == MyClass, true) assert_equal(dc.class == MyClass, true)
ex = RBA::DeviceExtractorDiode::new("myclass") ex = RBA::DeviceExtractorDiode::new("myclass")
ex.test_initialize(RBA::Netlist::new) dc = ex.default_device_class
assert_equal(ex.device_class.name, "myclass") assert_equal(dc.name, "myclass")
assert_equal(ex.device_class.class == MyClass, false) assert_equal(dc.class == MyClass, false)
assert_equal(ex.device_class.class == RBA::DeviceClassDiode, true) assert_equal(dc.class == RBA::DeviceClassDiode, true)
ex = RBA::DeviceExtractorDiode::new("myclass", MyFactory.new) ex = RBA::DeviceExtractorDiode::new("myclass", MyFactory.new)
ex.test_initialize(RBA::Netlist::new) dc = ex.default_device_class
assert_equal(ex.device_class.name, "myclass") assert_equal(dc.name, "myclass")
assert_equal(ex.device_class.class == MyClass, true) assert_equal(dc.class == MyClass, true)
ex = RBA::DeviceExtractorResistor::new("myclass", 1.0) ex = RBA::DeviceExtractorResistor::new("myclass", 1.0)
ex.test_initialize(RBA::Netlist::new) dc = ex.default_device_class
assert_equal(ex.device_class.name, "myclass") assert_equal(dc.name, "myclass")
assert_equal(ex.device_class.class == MyClass, false) assert_equal(dc.class == MyClass, false)
assert_equal(ex.device_class.class == RBA::DeviceClassResistor, true) assert_equal(dc.class == RBA::DeviceClassResistor, true)
ex = RBA::DeviceExtractorResistor::new("myclass", 1.0, MyFactory.new) ex = RBA::DeviceExtractorResistor::new("myclass", 1.0, MyFactory.new)
ex.test_initialize(RBA::Netlist::new) dc = ex.default_device_class
assert_equal(ex.device_class.name, "myclass") assert_equal(dc.name, "myclass")
assert_equal(ex.device_class.class == MyClass, true) assert_equal(dc.class == MyClass, true)
ex = RBA::DeviceExtractorResistorWithBulk::new("myclass", 1.0) ex = RBA::DeviceExtractorResistorWithBulk::new("myclass", 1.0)
ex.test_initialize(RBA::Netlist::new) dc = ex.default_device_class
assert_equal(ex.device_class.name, "myclass") assert_equal(dc.name, "myclass")
assert_equal(ex.device_class.class == MyClass, false) assert_equal(dc.class == MyClass, false)
assert_equal(ex.device_class.class == RBA::DeviceClassResistorWithBulk, true) assert_equal(dc.class == RBA::DeviceClassResistorWithBulk, true)
ex = RBA::DeviceExtractorResistorWithBulk::new("myclass", 1.0, MyFactory.new) ex = RBA::DeviceExtractorResistorWithBulk::new("myclass", 1.0, MyFactory.new)
ex.test_initialize(RBA::Netlist::new) dc = ex.default_device_class
assert_equal(ex.device_class.name, "myclass") assert_equal(dc.name, "myclass")
assert_equal(ex.device_class.class == MyClass, true) assert_equal(dc.class == MyClass, true)
ex = RBA::DeviceExtractorCapacitor::new("myclass", 1.0) ex = RBA::DeviceExtractorCapacitor::new("myclass", 1.0)
ex.test_initialize(RBA::Netlist::new) dc = ex.default_device_class
assert_equal(ex.device_class.name, "myclass") assert_equal(dc.name, "myclass")
assert_equal(ex.device_class.class == MyClass, false) assert_equal(dc.class == MyClass, false)
assert_equal(ex.device_class.class == RBA::DeviceClassCapacitor, true) assert_equal(dc.class == RBA::DeviceClassCapacitor, true)
ex = RBA::DeviceExtractorCapacitor::new("myclass", 1.0, MyFactory.new) ex = RBA::DeviceExtractorCapacitor::new("myclass", 1.0, MyFactory.new)
ex.test_initialize(RBA::Netlist::new) dc = ex.default_device_class
assert_equal(ex.device_class.name, "myclass") assert_equal(dc.name, "myclass")
assert_equal(ex.device_class.class == MyClass, true) assert_equal(dc.class == MyClass, true)
ex = RBA::DeviceExtractorCapacitorWithBulk::new("myclass", 1.0) ex = RBA::DeviceExtractorCapacitorWithBulk::new("myclass", 1.0)
ex.test_initialize(RBA::Netlist::new) dc = ex.default_device_class
assert_equal(ex.device_class.name, "myclass") assert_equal(dc.name, "myclass")
assert_equal(ex.device_class.class == MyClass, false) assert_equal(dc.class == MyClass, false)
assert_equal(ex.device_class.class == RBA::DeviceClassCapacitorWithBulk, true) assert_equal(dc.class == RBA::DeviceClassCapacitorWithBulk, true)
ex = RBA::DeviceExtractorCapacitorWithBulk::new("myclass", 1.0, MyFactory.new) ex = RBA::DeviceExtractorCapacitorWithBulk::new("myclass", 1.0, MyFactory.new)
ex.test_initialize(RBA::Netlist::new) dc = ex.default_device_class
assert_equal(ex.device_class.name, "myclass") assert_equal(dc.name, "myclass")
assert_equal(ex.device_class.class == MyClass, true) assert_equal(dc.class == MyClass, true)
end end