Alternative to polymorphism in non-OOP programming?
Asked Answered
O

3

6

Assume we have a drawing program with different elements, such as circle, rectangle, triangle and so on. Different kinds of objects that will all need similar function such as draw() to display themselves.

I wonder how would a programmer approach the problem that is nowadays typically solved by polymorphism, i.e. go through a collection of non-identical elements and invoke common functionality across the different objects.

One way that comes to mind is to have a struct with a function pointer to the appropriate function (or index in a function pointer array) as well as a void pointer to the actual instance, and pass the pointer which is cast to the proper type in the function. But that is just how I - a guy who is clueless on the subject would do it.

I do realize this might be a noobish question, but since I haven't been around in the "olden" days, I really wonder how was this problem tackled. What kind of approach was used in procedural programming and did it have a performance benefit, since we all do know polymorphism has an overhead even in fast languages like C++, due to the virtual method lookup.

Overly answered 23/8, 2012 at 21:22 Comment(5)
So your question is what was programming like before OO languages?I imaging working around concepts on how to structure their program that eventually evolved into OO? E.g. A class is typically same as a struct with the methods bundled.Janinajanine
@Janinajanine - no, my question is not intended to be that broad, I do realize what classes are behind the curtains, I was just wondering about that particular problem, mostly interested if there is a more efficient way that avoids the performance overhead of virtual methods.Overly
Maybe this helps: [#524533 [1]: #524533Pittman
This seems like an optimization question. Throwing away OO because it's theoretically slow is too hasty. In my opinion, you should only "optimize OO" when and where the profiler tells you to.Argyres
The way you describe doing it in a struct would be my first guess. You will see a lot of that in the Linux Kernel.Quach
Q
2

A really simple example.

If this interest you you can find more of this in the Linux Kernel.

#include <stdio.h>                                                              

struct shape {                                                                  
    void (*say_hello)(void);                                                    
};                                                                              

void circle_say_hello(void)                                                     
{                                                                               
    printf("Hi I am circle!\n");                                                
}                                                                               

void square_say_hello(void)                                                     
{                                                                               
    printf("Meh I am square.\n");                                               
}                                                                               

#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))                                  

int main(int argc, char *argv[])                                                
{                                                                               
    struct shape circle = { .say_hello = circle_say_hello, };                   
    struct shape square = { .say_hello = square_say_hello, };                   
    struct shape* shapes[] = {&circle, &square};                                
    int i;                                                                      

    for (i = 0; i < ARRAY_SIZE(shapes); i++) {                                  
        if (shapes[i] && shapes[i]->say_hello)                                  
            shapes[i]->say_hello();                                             
    }                                                                           

    return 0;                                                                   
}  
Quach answered 5/9, 2018 at 14:42 Comment(0)
R
0

In procedural languages such as C, this would be tackled by defining separate implementations of the draw() function for each custom data type (probably represented as a struct). Any common functionality would be factored out into a separate function which operated on shared elements of each struct (such as the x and y coordinate of the center of the object, which would appear in each one). From a code and functionality perspective, this isn't much different from the OOP layout utilizing polymorphism, where you still have to implement a shared draw() method in a base class and override it in the specific sub-class. In the case of a procedural language, we just wouldn't split these function definitions out into separate "objects".

There are some fancy ways to get object-like behavior out of a procedural language, such as a union type or a single monolithic type with extra booleans to determine if a particular element is in use. That would allow you to write a single draw() function that could perform logic switching based on which elements were enabled. In practice, the only place I have seen much of that is in CORBA-based systems where a program written in C had to mimic some of the behavior of an OOP language which was propagated through the IDL (i.e. translation of Java objects to constructs which could be decoded into C-style structs).

As for the overhead of virtual method lookup in languages such as C++ and Java, that is something that cannot be entirely avoided in an object-oriented language. It can be pretty well mitigated with proper use of the final keyword (which allows the compiler / JVM to optimize the method lookup tables).

Reassure answered 23/8, 2012 at 21:46 Comment(0)
J
0

This is not a direct answer to your example but an address to your comment, which shows a wrong perspective IMHO

I was just wondering about that particular problem, mostly interested if there is a more efficient way that avoids the performance overhead of virtual methods

There is something to understand here. Everything has a tradeoff. Design patterns and OO have all the known advantages we have come to love, but have disadvantages as well e.g. too many classes, memory overhead, performance overhead due to many method calls etc.

On the other hand the old "procedural" way had some advantages also to be objective; it was "simple" to code (no need to think how to design a system, just put everything in main) and had less overhead in many aspects (less memory overhead as less classes are needed and more compact objects -no need for virtual tables etc- and less method calls so perhaps better performance, no performance overhead for dynamic binding - whatever the overhead is nowadays anyway...-).

But it is not what the trade-offs of a particular problem instance are, it is what the experience has shown what is the proper way to build software. Reuse of code that is modular and assists in separate testing (quality assurance), readable, maintainable, flexible to extend are attributes that have been well understood that should be the main driver in software development.

So there are certain cases that a really good programmer in C/C++ could do the "old way" as you say, but is the performance benefit that it incur for this particular program worth the fact that no-one would be able to maintain or sustain it afterwards?

To give another similar example: You could ask in the same fashion?
Why multi-tier architectures in web development? Just put everything into one server and it will be A LOT FASTER since there will be no latency in querying the back-end and all the layers for the data of the UI or the network latency for a query of a remote database etc.
Sure, you have a point. But then ask your self, can this scale as load increases? The answer is no. So is scalability important to you or you want to keep the "put everything in one server" idea? If your income comes from e-sites, the fact that you can not serve more customers would not make your client happy just because you served the first 100 really fast...Anyway this is my opinion

Janinajanine answered 23/8, 2012 at 22:6 Comment(3)
I did not intend to question the merits of OOP at all. I just like to get informed that is all :)Overly
And as said I don't claim to address directly your question.I am just trying to show you a different perspective on what I understood is your point of view.Unless I didn't get your thinking and my answer is completely out of context (so I apologize in this case)Janinajanine
Don't sweat it, surely, I was already aware of most of the stuff you said, but I appreciate the time and effort you invested and no doubt your post will end up being useful to the community :)Overly

© 2022 - 2025 — McMap. All rights reserved.