1. ## Question about multiple classes

Hi, I've planned to create a math library, containing a lot of different classes, basically starting with the most basic classes as natural numbers or positive integers, then negative integers, and zero. Then integers, which is a superset of these three. Then non-integer rational numbers, which uses an integer as the numerator and a natural number as the denominator. Then rational numbers, which is a superset to integers and non-integer rational numbers. The classes for positive integers, zero, negative integers and for non-integer rational numbers will all be concrete (not abstract), while integers and rational numbers will both be abstract classes. Then it is also possible to add classes for more complicated objects, like various kinds of expressions for irrational numbers, complex numbers, vectors and matrices, etc. etc. Finally, I will also have an abstract superclass which will be inherited by all math classes. Ideally, an arbitrary math object will be an object of that class.

Now to the problem: Operations between different math objects, such as the plus operation, will have to use many different functions, maybe as many as one function for each combination of the class types for the two arguments. Say for example that I have 5 different kinds of math objects that are non-abstract; maybe I will need to have 5*5 = 25 versions of the plus operation. How can I implement the plus operation in the code; as a virtual function? What would it look like? Say I have two objects of the super class; how would I perform the operation?

My idea was to in some way give each concrete class a number, which would work as a key to a two-dimensional array (5*5 in size, if there is 5 concrete classes) containing a lot of function pointers to different versions of the plus operation. For example, if the objects a and b are of subclasses with numbers 2 and 3, the place in the array which holds the function pointer to the operation that should be used on a and b, will have the coordinates (2, 3). If the function pointer is NULL it means that the operation is not supported between those two subclasses. I just don't know how to give each class a unique number; it feels unnecessary to use a virtual function for that reason. Isn't it possible to have a virtual constant for a class, that is stored in the v-table?

2. Out of curiosity, what are you goals of this?

Reason I ask is that there are plenty of math libraries available so if you are making it for usage purposes then don't waste the time, just use a pre-made one. If you are making it for learning purposes then right on. However, it kind of sounds like you want to start with remaking what is already defined, like an integer, or floating point number, etc.

Also...

... basically starting with the most basic classes as natural numbers or positive integers, then negative integers, and zero. Then integers, which is a superset of these three...
Integers are natural numbers, and to have them inherit a natural number class makes no sense since you have a keyword called int.

This is just my opinion, and I speak out of turn and don't always have the best advice. However, I would make what is not already readily available, ie. a complex number class that has a real and imaginary part. Even that is still available in already made math libraries, though. So in short, if you want to make an integer class, why? Use the keyword int instead

3. Originally Posted by scwizzo
Integers are natural numbers, and to have them inherit a natural number class makes no sense since you have a keyword called int.
No, depending on your definition, natural numbers are positive or non-negative integers, so it is not the case that integers are natural numbers.

TriKri, do you have some kind of bignum library in mind? If not, I agree that you might as well build on the built-in integer types directly.

4. Originally Posted by scwizzo
Out of curiosity, what are you goals of this?
I'm actually making it more for learning purpose. But who knows, maybe it can become useful. However, the problem I introduced is one of the more difficult ones, which I don't know how to solve in the best way. I think I know how I can solve it in one way, but I wanted to hear your opinion before I started with it. How would you do? And I also still wonder, is it possible to store constants or variables in the v-table?

And about the number classes - I want to make a library for arbitrary numbers. In other words, the integers should be able to be arbitrarily large, which an int can't be. Floating point numbers are also limited, both in size and in precision. The rational number class will be able to represent any rational number as long as the memory is sufficient. However, I guess there already are libraries for that purpose, like the GNU MP Bignum Library

Also, I should correct my previous post: Ideally, an arbitrary math object will not be an object of the superclass of all math classes, but an object of a pointer class containing a smart pointer to an object of that superclass. In that way, when two objects of the pointer class are operated on, the return value will also be an object of the pointer class. The operator will simply just create a new object of the appropriate class for representing the resulting number, and make the pointer class point to it. I guess this is more or less how it works in most scripted languages like JavaScript or php, or...?

For example, if an object of the pointer class is created from the int 1, a new integer object will be created. If a division is made between two pointer objects, a new non-integer rational number object will be created if the quotient is not an integer; otherwise a new integer object will be created. The pointer to the newly created object is then put into a smart pointer, which in turn is put into the object of the pointer class that will be returned.

For the reason above, I just realize that I can't operate directly on two integer objects, at least not when it comes to division, since I don't know the class of the object to be returned; it may be an integer, and it may not be. So in this case, it's probably better not to create operators between the different classes, at least not the division operator between two integers, but to leave everything to be handled by the pointer class.

But say that every operation between two objects could be defined with a determined resulting type. How would two objects of the pointer class be added to each other (for example) in the best way? How would the plus operation between two pointer objects know which plus operator to call? I still have my idea that a constant could be stored in the virtual table of each class, to let the pointer class know what type of object it is pointing to. Then, how would the constant be stored in the v-table? Or is it better solved by using virtual functions in some way?

Originally Posted by laserlight
TriKri, do you have some kind of bignum library in mind? If not, I agree that you might as well build on the built-in integer types directly.
Yes, and I guess there are plenty of those out there. Do you have any special in mind which you can recommend?

5. Please note that object-oriented design is probably not the right way of doing a math library. Relations between various number types have a tendency to break the OOD inheritance models in ugly, subtle ways unless the programmer really knows what he's doing. The lack of a proper multiple dispatch mechanism in most OO languages makes working with a mix of multiple types a pain. And of course, the fact that polymorphic OO designs usually rely on dynamic dispatch and dynamically allocated objects wreaks absolute havoc with the performance of such a low-level domain as mathematics.

Generic programming has proven more successful at tackling these problems in C++. Concepts, with their subtle difference in meaning from abstract interfaces, can better model the properties of numeric types. C++'s static polymorphism has no problem with multiple dispatch. Generic algorithms are selected at compile time and can be inlined and optimized, leading to far better performance. The types involved are usually value types, and so will often avoid the need for dynamic allocation.

6. I'm not really sure what you mean, do you have an example? Can't object-oriented programming be generic? According to Wikipedia, generic programming in C++ seems to be when you make use of templates, but templates can be used on objects too, right?

7. Generic programming is a paradigm supported by surprisingly few languages. Among those are C++, D, and Haskell. GP usually uses static polymorphism techniques (templates in C++), where hierarchies are created with concepts (implicitly defined through usage and documentation in C++, type classes in Haskell) instead of object-oriented interfaces. Concepts differ from interfaces in some important aspects. One is that a concept is not a type; you can't have a variable of a concept's type (or reference/pointer to it). Another is that a concept can mandate more than your average OO interface. An interface contains functions. A concept can require functions, static functions, and so-called "associated types", i.e. types that are related to the concept. (I'll give you an example in a moment.) Moreover, concepts aren't necessarily unary; a concept can be binary, trinary, or n-ary. For example, Comparable could be an unary concept (two objects of type T that models Comparable can be compared), but it could also be a binary concept (an object of type T can be compared with an object of type U if T and U together model Comparable).

Take, for example, a simplified version of your math library, which has only two types, Integer and Real, plus a unifying concept/interface, Number.

In classic OOP, you would have something like this (I'll use Java syntax here):
Code:
```interface Number {
abstract Number complement();
abstract Number absolute();
abstract Number add(Number n); // ???
abstract Number zero(); // ???
}```
We already hit the limitations of OO in most current languages here. How do you properly implement add(Number)? You need multiple dispatch to account for the two parameters. If you look at the Wikipedia article for MD, you'll find that you probably don't know most of the languages that actually support it. (Prominent members are Common Lisp, Perl 6 and C# 4.0. Incidentally, only one of those is older than a year.)
What about zero(), i.e. a way of obtaining the number's zero-element? That ought to be a static function, but what languages allow virtual static functions? The very idea is a contradiction in terms.
Here's another issue: let's do this thing in C++:
Code:
```struct Number {
virtual ptr<Number> complement() = 0;
};```
What is ptr? Is it a raw pointer? If so, who is responsible for freeing the memory?

OK, enough of that. Let's look at the generic version of this. Remember that I said that concepts are specified in documentation? (C++0x was supposed to have a programmatic way of specifying concepts. But that was not ready and therefore was pulled.)

Given a type T that models Number, and instances t, t1 and t2 of this type, the following expressions shall be valid:
Expression: numeric_traits<T>::zero()
Type: T
Semantics: the additive identity element of T

Expression: t1 + t2
Type: T
Semantics: an associative, commutative binary operation with the identity element numeric_traits<T>::zero()

Expression: -t
Type: T
Semantics: t + -t == numeric_traits<T>::zero(), that is, -t yields the complement of t in regards to the + operation
There are some interesting aspects to this. For example, we have restricted addition to adding two objects of the same type, i.e. Real + Integer is not allowed by this concept. But we could add a binary concept Addable that requires its two types to be addable, and have Real and Integer model that. In fact, it is generally better to define a number of smaller concepts and have the big concepts tie them together. It gives us greater flexibility in defining the requirements of functions operating on these objects. For example, the above Number concept is already too restrictive: natural numbers cannot provide unary minus, and depending on your definition do not have zero() either.
An interesting ability of concepts is retroactive modelling. You might observe that the built-in type int already fulfils the requirements of the Number concept, except for that zero() function. But we can add that in after the fact.
Code:
```template <typename T> struct numeric_traits; // basic declaration, specialize it!
template <> struct numeric_traits<int> { int zero() { return 0; } };```
With inheritance-based object-oriented design, you can't retroactively model. If you want to tie that third-party bignum class into your design, you'll have to write a wrapper class that derives from your interfaces and implements the operations. This leads to runtime cost, as you have to wrap and unwrap these objects, as well as complexity cost, because you have to keep track of the lifetime of the wrappers and their inner objects.
With concepts, adapting the bignum class to the concept would probably come down to implementing a few free functions, which the compiler can probably inline and optimize away.
Also, did you notice that the concept version of the code dealt with objects by value, and not by reference? In C++, that's a property you usually want.

8. Elysia: I still don't see the drawbacks with using OOP. Wouldn't the methods that are common to all graphs be implemented in the base class, while the methods that differ among the different graphs be implemented in the derived classes?

9. CrnedBee: I'm not really sure if I understand everything you wrote; for example, do you mean C++ templates when you refer to concepts, and C++ classes when you refer to interfaces?

Then either have I misunderstood your point, because I don't really see how you mean that I should use templates in my library instead of OOP, or you have understood they way in which the library is supposed to be used. Let me give you a more thorough description:

The only class in this library that is going to be used really much is a universal class, which is a kind of container class that just contains pointer to a superclass of all other classes. As an example could the superclass have been the rational number class in this case. When an operation between two objects takes place, what is given back is an object of the universal class, pointing to a new object of the appropriate kind.

I make it like this simply so that an expressions from user input can be evaluated at runtime, without having any previous knowledge about what kind of object he expects the expression to be evaluated to. For example, he might enter "100" which is a natural number; in that case it should be stored as a natural number. Or he might enter a "10/3", and then it should be stored as a non-integer rational number (a subclass to the rational number class). Or he might enters "(10/3)*(3/2)", where the two fractions evaluates to non-integer rational numbers, and the multiplication evaluates to a natural number.

This means that when an operation is performed, I probably don't know the types of the operands at compile time. But for the cases when the numbers are not encapsulated inside of containers, and I do know the types, it's probably a good idea to use templates the way you suggested.

I'm still not sure if I have properly understood everything you wrote though.

Originally Posted by CornedBee
In fact, it is generally better to define a number of smaller concepts and have the big concepts tie them together. It gives us greater flexibility in defining the requirements of functions operating on these objects. For example, the above Number concept is already too restrictive: natural numbers cannot provide unary minus, and depending on your definition do not have zero() either.
I don't really see you point; would you have used the natural number class from the beginning if you where to implement the zero() and complement() functions?

In my case, there will be another class for negative integers which will be used to complement the natural numbers, and yet another class for zero. And even though it takes three classes to represent all of them, I will still have a universal class which will have a complement to all its instances, and where the zero element is also an instance of the class.

Originally Posted by CornedBee
Also, did you notice that the concept version of the code dealt with objects by value, and not by reference? In C++, that's a property you usually want.
Once again I'm not really sure what you mean; I don't see any example where you deal with the objects by reference. Or how would this apply to my case?

10. Originally Posted by TriKri
CrnedBee: I'm not really sure if I understand everything you wrote; for example, do you mean C++ templates when you refer to concepts, and C++ classes when you refer to interfaces?
A concept is the set of requirements (syntactic and semantic) that a template puts on its arguments. For example, a type that you want to store in a std::vector needs to fulfill at least these requirements:
- Have a copy constructor.
- Have a copy assignment operator.
- Don't throw from the destructor.
The first two requirements are CopyConstructible and CopyAssignable, respectively (in the terms of the standard), and together form the Copyable concept. The third requirement is NothrowDestructible.
These concepts only exist in documentation (namely, the C++ standard), but they are nonetheless an essential part of the generic programming concept that the STL is based on.

An interface is an object-oriented idea. In C++, it is modeled by a class which contains no data and only pure virtual functions. It is used for a similar purpose as a concept, but because it has to be runtime-polymorphic, it is less powerful.

The only class in this library that is going to be used really much is a universal class, which is a kind of container class that just contains pointer to a superclass of all other classes.
So you've got a smart pointer exposing the interface. But that's actually irrelevant to the overall design.
The superclass here is the relevant part. This superclass is an interface, even if you make it "dirty" (i.e. have it contain data and/or non-pure functions). Then you have a number of derived classes.

I make it like this simply so that an expressions from user input can be evaluated at runtime, without having any previous knowledge about what kind of object he expects the expression to be evaluated to. For example, he might enter "100" which is a natural number; in that case it should be stored as a natural number. Or he might enter a "10/3", and then it should be stored as a non-integer rational number (a subclass to the rational number class). Or he might enters "(10/3)*(3/2)", where the two fractions evaluates to non-integer rational numbers, and the multiplication evaluates to a natural number.
This sounds nice in theory, but it's not exactly practical. You go through a LOT of effort, for zero gain.
Consider this: for the user of the program, if everything works as it should, the type switching behind the scenes is invisible. All he sees are printed results, not what class you use internally for it. If you want to print different kinds of numbers differently, then that's a distinction in printing only, but shouldn't affect calculations.
A programmer, a user of the library, would always use your universal class, so he, too, doesn't notice your type switching. Nor does it have any effect on him.
What, then, is the use of your type switching? Only some perceived elegance in the design.
What is the cost? You have to write lots of very similar classes (NaturalNumber, WholeNumber, RationalNumber, RealNumber), not to mention the scaffolding (NumberSuperclass, UniversalNumber). Your design requires multimethods, which are always problematic. (Already you're thinking about writing an RTTI system outside of C++'s provided facilities.) Performance is catastrophic - every single little number used will result in a dynamic allocation, not to mention all the necessary decision-making before you can do something as simple as adding two numbers together. And there are probably more costs that I haven't even thought of.

And all this when the only distinction between the various number types appears when printing the number.
Even if you want to preserve the precision of rational numbers when staying in that domain, you still shouldn't use a class hierarchy here. Simply have a single Number class. If will store a double or a boost::rational<int>, in a boost::variant. This gives you everything you need for both calculations in the real number space, and precise calculations in the rational space if both arguments are rationals. Whole numbers are doubles that do not have a fractional part, or rationals with a denominator of 1. Natural numbers are non-negative whole numbers. Those are dynamic properties and should be treated as such.

I don't really see you point; would you have used the natural number class from the beginning if you where to implement the zero() and complement() functions?
I don't know what you mean.

Once again I'm not really sure what you mean; I don't see any example where you deal with the objects by reference. Or how would this apply to my case?
Your universal class is a reference in disguise.

11. Originally Posted by CornedBee
An interface is an object-oriented idea. In C++, it is modeled by a class which contains no data and only pure virtual functions. It is used for a similar purpose as a concept, but because it has to be runtime-polymorphic, it is less powerful.
You suggest that I should use boost::variant, but isn't that to runtime-polymorphic, or has to decide in runtime what type it is?

Originally Posted by CornedBee
What, then, is the use of your type switching? Only some perceived elegance in the design.
What is the cost? You have to write lots of very similar classes (NaturalNumber, WholeNumber, RationalNumber, RealNumber)
I need to have all these types and the type switching since I want to be able to represent the numbers in different ways, which I have already mentioned. I also want to be able to extend it, and to use even more classes in the future.

Originally Posted by CornedBee
Your design requires multimethods, which are always problematic. (Already you're thinking about writing an RTTI system outside of C++'s provided facilities.)
I don't see this as a problem. I know how I'm going to implement it, and it will give me a very flexible library where it is easy to add new classes and new operations. I can easily check which operations that are not defined (such as division by zero), by looking in a table and see if there is a function for the specific operation. I can also see if the operation will result in a syntax error (like addition between a number and a matrix).

Originally Posted by CornedBee
Performance is catastrophic - every single little number used will result in a dynamic allocation
Originally Posted by CornedBee
Your universal class is a reference in disguise.
You are right; maybe I shouldn't have a pointer in the universal class. It is possible to use a union instead. I want to avoid using boost::variant in this project, since it will disable me from indexing the classes.

Originally Posted by CornedBee
not to mention all the necessary decision-making before you can do something as simple as adding two numbers together. And there are probably more costs that I haven't even thought of.
If the problem is the running time, and you suggest using boost::variant, I don't see how that would be an improvement. What decision making is done in boost::variant in order to determine the appropriate operation to use? Because I guess variant uses some own kind of RTTI, which will require the same amount of decision making, now hidden from me instead of from the user of the library.

Originally Posted by CornedBee
Whole numbers are doubles that do not have a fractional part, or rationals with a denominator of 1.
I have mentioned before that the natural number class should be able to represent any natural numbers, i.e. arbitrarily big numbers, which is not possible using doubles. And doubles are meant for representing floating point numbers. That is why I want to use the GNU MP Bignum library. And I can't represent a natural number using a rational number, since the rational number itself consists of two natural numbers.

Originally Posted by CornedBee
I don't know what you mean.
Well, if one wants to have a class that follows the Number concept you proposed, a natural number shouldn't be used, but an object from the universal class.

12. Originally Posted by TriKri
You suggest that I should use boost::variant, but isn't that to runtime-polymorphic, or has to decide in runtime what type it is?
Yes, but in a very limited way. I only suggest this because you seem to want to represent rational numbers precisely, while at the same time have the ability to hold irrational numbers. If either of these requirements is not necessary, you should use only one underlying datatype anyway. In any case, you should keep the number of different types very small.

I need to have all these types and the type switching since I want to be able to represent the numbers in different ways, which I have already mentioned.

I also want to be able to extend it, and to use even more classes in the future.
Systems with multiple dispatch are not easily extensible.

I don't see this as a problem. I know how I'm going to implement it, and it will give me a very flexible library where it is easy to add new classes and new operations.
That's what you think now. The reality is that every new class requires you to add N*M functions, where N is the number of existing classes and M the number of operations, while every new operation requires you to add Nē functions. The maintenance effort of this is considerable. The design isn't flexible, either. You depend on the capabilities of the universal number class, which will quickly become a big fat monolith and maintenance nightmare.

I can easily check which operations that are not defined (such as division by zero), by looking in a table and see if there is a function for the specific operation.
That's assuming that every number subqualification that's relevant in the validity of all future operations you'll add is represented by its own class. Do you represent numbers in the range -1..1 with their own class? Inverse sine and cosine are only defined on that range. Will it be three classes for the separation of negative, zero and positive? 8 classes to distinguish between the whole number -1, the rational numbers in (-1,0), the irrational numbers in (-1,0), zero, the irrational numbers in (0,1), the rational numbers in (0,1) and the natural number 1?

I can also see if the operation will result in a syntax error (like addition between a number and a matrix).
Pleeeease tell me you're not thinking about extending your "universal number" to things like vectors and matrices! Even complex numbers violate enough assumptions about real numbers that using the same type for them is disastrous.

You are right; maybe I shouldn't have a pointer in the universal class. It is possible to use a union instead. I want to avoid using boost::variant in this project, since it will disable me from indexing the classes.
It will what? No, variant is absolutely perfect for this purpose, since it provides the index for you.

If the problem is the running time, and you suggest using boost::variant, I don't see how that would be an improvement. What decision making is done in boost::variant in order to determine the appropriate operation to use? Because I guess variant uses some own kind of RTTI, which will require the same amount of decision making, now hidden from me instead of from the user of the library.
Keep in mind that my fundamental suggestion is to keep the number of types as low as possible. One type is ideal. Two types are acceptable. If there are two types, variant is the right choice because it is a ready-made, minimal solution. Anything else means more code that you have to write, for no gain.

I have mentioned before that the natural number class should be able to represent any natural numbers, i.e. arbitrarily big numbers, which is not possible using doubles. And doubles are meant for representing floating point numbers. That is why I want to use the GNU MP Bignum library.
So use an arbitrary-precision real number class.

And I can't represent a natural number using a rational number, since the rational number itself consists of two natural numbers.
Now that's nonsense. Who says that the internal representation of the rational has to use your number classes? In fact, that would be an absolutely horrible idea.

13. I have explained my thoughts and I will not develop them anymore; so I'm gonna drop the conversation here. I know what I do, and if you don't believe in it that's up to you. I have already been helped with what the thread from the beginning was all about. Thank you.

14. You think you know better than a veteran of the board? Honestly?
How you do it is entirely up to you, but I am suggesting that you should not act or "come off" as superior to a veteran on this board who has years of experience in these matters.

15. I felt the conversation had kind of taken a wrong turn; you seemed to question the very way I wanted to use the library in. I don't question either your or CornedBee's skills; I believe you both are very experienced and skillful programmers. But if you want to help me, you can't tell me that what I'm doing is a bad or a horrible idea! I'm also a good programmer.

That's why I felt the conversation had kind of lost it's way. Do you agree? I listened to what CornedBee wrote, and some of it were good but some I felt I had a different opinion about, and yes - I questioned some of it, but far from everything. I don't mean to say that everything sucked, because that was really not the case. I'm sorry if I made it sound like it.