Class replacement and limiting inheritance

I have what may be a very unique problem to solve.

I am writing a "modeling" program that is to be used to model various types of concepts and principles. I have a generic class that is meant to be used as a base class for providing these different types of models that I call IParadigm. Within the program is a standard set of modelling systems derived from this base class that are to be replaced by various plugins. When a plugin is not present, however, the "standard" plugin is used.

Since this program will be modelling scientific principles, I will use two examples from science to illustrate my problem: particles (such as electrons) and cells.

Both of the "generic" or "standard" versions of these classes will be derived from IParadigm. We will call them StandardParticleModel and GenericCell for the sake of this example.

With particles in physics, there are no variations to the standard particle model. I.E. there is only one model and if someone writes a plugin (to illustrate radiation for example), their plugin is supposed to inherit the StandardParticleModel class and REPLACE the "standard" (generic) StandardParticleModel class so that it supersedes all "standard" functionality.

This replacement functionality will not work with cells since not only do we have different types of cells, but we even have different types of animal cells. (Skin cells, nerve cells, muscle cells etc...). Therefore GenericCell must allow standard inheritance.

I can probably come up with solution on my own involving a complex set of rules and class instance pointers but I was wondering if there was any new (or old) C++ conventions to make this odd setup easier to manage. I need to make an informed decision about how to proceed. Here are my questions:

1. Is there any kind of standard for replacing all functionality of a class using inheritance (or some other technique)? Or is that something that will need to be done programmatically by redirection and/or using pointers?

2. If I can implement replacement directly, can I do it dynamically so that inheriting StandardParticleModel will replace the generic class at runtime and inheriting GenericCell will not?

3. Any other basic recommendations/insights on how to go about this?
Last edited on
Something liker the locale/facet framework in the standard library, perhaps?

The class std::locale::id provides implementation-specific identification of a locale facet. Each class derived from std::locale::facet must have a public static member of type std::locale::id and each std::locale object maintains a list of facets it implements, indexed by their ids.

Facets with the same id belong to the same facet category and replace each other when added to a locale object.

http://en.cppreference.com/w/cpp/locale/locale/id


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include <iostream>
#include <locale>
#include <string>

struct my_new_facet : std::locale::facet // belongs to a new facet category
{
    my_new_facet( std::size_t refcnt = 0 ) : facet(refcnt) {}

    void say_hello() const { std::cout << "my_new_facet says hello\n" ; }

    static std::locale::id id; // id of the new facet category
};

std::locale::id my_new_facet::id;

struct my_numpunct : std::numpunct<char> // belongs to the std::numpunct<char> category
{
    protected: virtual std::string do_grouping() const override { return "\3\2"; }

    // same id as std::numpunct<char>
};

int main()
{
    std::locale locale ;
    locale = std::locale( locale, new my_new_facet ); // add a new facet of type my_new_facet
    locale = std::locale( locale, new my_numpunct ); // replace the existing std::numpunct<char>

    std::use_facet<my_new_facet>(locale).say_hello() ;

    std::cout.imbue(locale) ;
    std::cout << 123456789 << '\n' ; // uses my_numpunct
}

http://coliru.stacked-crooked.com/a/d8c65aa7f00ed3f8
So basically you are you suggesting I should implement a static reference counter in the base class?

The question then becomes an issue of a potential need for static virtual methods (which I have needed before for a vaguely similar situation). The only solution to this that I am aware of is the curiously recurring template. I suppose that may be an acceptable solution. I will look into this and see if it can work.
Just my 2 cents worth here :+)

Maybe Policy Design? Thinking that things can be combined in lots of different ways.

https://en.wikipedia.org/wiki/Policy-based_design

A somewhat similar idea is the strategy Design Pattern:

https://en.wikipedia.org/wiki/Strategy_pattern

Btw, it was JLBorges who first introduced that Policy idea to me :+)
Wow! Thanks. Some (somewhat) new approaches/tools to help come up with something. I was already vaguely familiar with approaches similar to the strategy pattern because I have come up with similar approaches to peculiar problems... but the policy design is completely new and a very interesting idea to use in design. I never really thought of inheriting from a template specified class.
> So basically you are you suggesting I should implement a static reference counter in the base class?

The static member std::locale::id id; is the identity of the facet category. The essence of the design is:
Facets with the same id belong to the same facet category and replace each other when added to a locale object.


That facets are reference counted (life-time management of facets) are incidental to the design issue under discussion.
Ok. I have decided that using a combination of "policy" based design and curiously recurring templates may do the trick. However I am a little unclear of how to refer to the functions from the "policy" class. The example in the wiki article above uses the "using" statement. However, can I use the using statement when the method has parameters? Here is what I have so far.

FYI I use #defines to allow me to organize code using tags in class definitions (example #define inl_props)

Here is the base class IParadigm:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
template<typename DerivedClass>
class MODELLER_EXPORT IParadigm
{
	// note this is an incomplete class...

public inl_props:
	// reusable means that this paradigm may be inherited rather than replaced
	inline static const bool isReusable() { return DerivedClass::isReusable(); }
	// fundamental means that modelling a more basic division is not possible.  This is the "bottom-most turtle"; i.e. end of the line.
	inline static const bool isFundamental() { return DerivedClass::isReusable(); }
	inline static const StandardParadigms standardID() { return DerivedClass::standardID(); }

public interfaces:
	virtual void getModelRequest(ParadigmModelRequest* request) = 0;
	virtual void getFactoryModelRequest(ParadigmModelFactoryRequest* request) = 0;

};


Here are the two derived types of paradigms. Note the first one where the class is meant to replace the original paradigm is a "policy" using the policy design standard. My question lies in the comments in this particular class.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// the replaceable Paradigm
template<typename ReplacingParadigm>
class MODELLER_EXPORT ReplaceableParadigm : ReplacingParadigm // where ReplacingParadigm : IParadigm<ReplacingParadigm>
{
	using ReplacingParadigm::isFundamental;

	// ARE THESE LEGAL??
	using ReplacingParadigm::getModelRequest;
	using ReplacingParadigm::getFactoryModelRequest;

	static const bool isReusable() { return false; }
};

// the reusable Paradigm
class MODELLER_EXPORT ReusableParadigm : IParadigm<ReusableParadigm>
{
private fields:
	unsigned __int16 mFlags;

public:
	static const bool isReusable() { return true; }
	static const bool isFundamental() { return (bool)(mFlags & FLAGMASK_FUNDAMENTAL); }
};
Last edited on
// ARE THESE LEGAL??


I think of it like this:

The using declaration is described here, about halfway down the page: http://en.cppreference.com/w/cpp/language/namespace

cpprefrence wrote:
Using-declarations

Introduces a name that is defined elsewhere into the declarative region where this using-declaration appears.
using typename(optional) nested-name-specifier unqualified-id ;
nested-name-specifier - a sequence of names and scope resolution operators ::, ending with a scope resolution operator. A single :: refers to the global namespace.
unqualified-id - an id-expression
typename - the keyword typename may be used as necessary to resolve dependent names, when the using-declaration introduces a member type from a base class into a class template

Using-declarations can be used to introduce namespace members into other namespaces and block scopes, or to introduce base class members into derived class definitions.

For the use in derived class definitions, see using declaration.

Names introduced into a namespace scope by a using-declaration can be used just like any other names, including qualified lookup from other scopes:


Also:

template<typename ReplacingParadigm> could have been written template<class T> . I am sure you know that typename and class have the same meaning in template parameters. Then we have: using T::getModelRequest; So in main where the template is instantiated, T (or ReplacingParadigm) is replaced by the concrete Policy, and getModelRequest is the member function that each concrete Policy class has.

Hope this helps :+)

So basically parameter type qualifiers are irrelevant? I assume then that overloading a function that is used in this way will cause a problem?

or does the compiler create any used overloads dynamically?
Last edited on
In addition to the above question, I have one more before I can finish designing these classes

I have realized that the two classes above are not enough. I need to make two classes for the "standard" paradigms and two abstract classes that inherit these classes for any plugin paradigms where:

Standard Paradigms (shipped with the core product) are classes that directly inherit:

ReplacableParadigm
ReusableParadigm


Plugin classes are classes that are based on/work with the above...

INewParadigm is the base class for classes that replace a ReplaceableParadigm
IExtendedParadigm is the base class for classes are meant to create a specialized ReusableParadigm

The issue is that I need to make sure that the compiler knows that the class used for a template is of a specific type; otherwise it needs to generate an error.

For example:
INewParadigm<class newClass> needs to make sure that newClass is derived from a class that is derived from ReplacableParadigm. (newClass is the "policy" class)

Is there a way to do this in the template definition? I would prefer to do it natively (without using some third party plugin).

EDIT: Added specific example below

For example, here are a set of classes and their types.

class AnimalCell : ReusableParadigm; (from the standard set inside the core product)
class NerveCellPlugin : IExtendedParadigm<AnimalCell>; (a plugin used by the product)

I would like to make sure (at compile time) that AnimalCell is indeed derived from ReusableParadigm
Last edited on
So basically parameter type qualifiers are irrelevant? I assume then that overloading a function that is used in this way will cause a problem?

or does the compiler create any used overloads dynamically?


I don't see why one can't do function overloads in templates, just the same as normal. Not sure how template parameters could be seen as being irrelevant: can you explain what you mean there? The template parameters are just labels that are replaced with the actual type upon instantiation. The compiler does create instantiations dynamically, it is an important concept - it only creates the ones it needs.


I would like to make sure (at compile time) that AnimalCell is indeed derived from ReusableParadigm


Type traits?

http://en.cppreference.com/w/cpp/types/is_base_of

If you go up 1 level from that link, there are heaps of other very handy type traits :+)

With all these cell types, I just wanted to make sure that they are different from an OOP point of view? I hope that doesn't sound like a dumb question: obviously a Nerve cell is different to a Muscle cell. But do they have different behaviour (specialisation) as opposed to them all having the same attributes that may have different values? They probably do, but I thought I would ask anyway. A different example: In a Role Playing Game, we might have different types of Weapons, but I don't create separate classes for each type of Sword/Dagger. They would all have the same attributes: Name, Length, bool OneHanded, NumEdges etcetera. In the case of Rifles/Pistol there might be a need because they take different ammunition.
For the first quote...
Say for example that instead of using two different functions for the model requests, I overloaded the same function:

1
2
3
4
5
...
public interfaces:
	virtual void getModelRequest(ParadigmModelRequest* request) = 0;
	virtual void getModelRequest(ParadigmModelFactoryRequest* request) = 0
...


How does this know which to use when no parameters are specified? OR does the compiler just overwrite the one(s) that are used?

using ReplacingParadigm::getModelRequest;

As for type traits, thanks. I actually stumbled across that yesterday. I will have to check the others out as well.

What are you saying about types and classes makes sense. I should keep that in mind when coming up with the list of "standard" cell types. However, with nerve cells, there are definitely behavior differences. In addition, the whole purpose of the collection of "Paradigms" is to provide modelling behavior and structural organization. From that perspective there are some important differences with nerve cells as well. That is also the advantage of the design... I don't know all the differences as biology is not my specialty. (I am a science teacher). So someone writing a plugin to model a more accurate/specialized version has the framework they need to do it.
Last edited on
How does this know which to use when no parameters are specified?


With the example code on the wiki page, there is a using statement for the print function, and I that is there so the print function can be called a few lines later in the run function. If that function is overloaded, then I imagine the number and type of arguments will determine which function is called - just like normal overloading. Mind you , I haven't tested that.

With the class behaviour, I thought that would be the case.

With the standard cell types that might have the same attributes, I wonder is there a need to be able to overload functions with them? If it's one class, it's one type, so doing overloading won't work. As an example, consider a Circular Arc class. Variables such as Radius, ArcLength, BeginAngle, EndAngle all hold a double, and there is a similar idea for points. We need them to be different types so we can overload the ctors. Rather than make a class for each one, I managed to implement this using boost::mpl::int_n , the meta programming library:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
namespace bm = boost::mpl;

using Radius_t = bm::int_n<1>;
using ArcLength_t = bm::int_n<2>;

// T is not used, it's only there to make a distinct type
template <typename T, typename U = double>
class SingleValue {
private:
   U value;
public
   explicit SingleValue(double valueArg) : value(valueArg) {}
};


// instantiation

SingleValue<Radius_t> MyRadius(10.0);


So if we do this for the various variables we need for our ctor, we can happily overload with various combinations because they are now all distinct types.

I was thinking you may have a need for this type of thing with your cell types, possibly :+)

Regards :+)
Thanks for the additional resources. By way of explanation though, the "Paradigm" is not a model. It is a modelling system or framework of models. Hence the overloaded getModelRequest (or factory model request).

Keep in mind that cell itself is made of components within. One can request a model of the entire cell, or any of the items within it. The cell model is less complex than some of the other "Paradigms" that will be available. Some models must be dynamically created based on the number of components needed. This is why there are two types of model requests (regular and factory).

The factory request is used when the model is dynamically created whereas the regular request is for when the model is a standard component.
Ok, it all sounds good :+) I am sure you will have a great and enjoyable project. Regards :+)
Topic archived. No new replies allowed.