Re: C++ implementation for C API ---- converting legacy C code to C++



Responding to Masood...

In this day and age, you never say no to any work that is thrown at you
---- so when I was offered this short-term contract to convert legacy C
code to C++, I did not say no.  Personally I believed that it was a
somewhat futile exercise since one of the main requirements was for the
existing API (a functional interface written in C) to remain the same.
I would have much rathered that the mandate be ab-initio, but that was
not the case here.  My client had a bad experience with OO, and they
wanted to re-tread this path very very carefully.  They were convinced
that a "phased approach" to OO worked much better for them.

I basically agree with Nolan. Don't convert the C code; rewrite in C++ from the ground up.


Having said that, there is nothing wrong with C APIs around large scale functionality. One implements the functionality in an OOPL and then essentially puts a GoF Facade pattern around it that just happens to present a C API. The customer is always right and it is pretty easy to provide such an API for C/C++. (It is trickier in an interpreted environment.)


I tried to argue (although not very passionately) that functional and OO design together provide a rather immiscible combination --- one which nobody is happy with. However, for someone who was prepared to dig trenches, I did not make a very big deal out of it. Since the API had to remain the same, and there was not enough time for a detailed use-case/OO analysis, here's how we got about this task.

If you think of the API as a Facade pattern there should be no problem. Think of the Facade as an interface that decouples the two paradigm domains. IOW, it /allows/ you to create the software using a different paradigm.



1) Studied the current implementation of the APIs --- if it sounded "coherent", and if it could be easily converted into a method on a class, we went ahead and did that

2) If the API implementation did bot seem coherent, we chopped it into
coherent parts, and each of these parts was then converted into a
method on a class.  To make sure that there was no proliferation of
classes, we kept track of all the classes that had been identified
during the process, and tried to reuse previously identified classes as
much as possible.  (If I had the time and the mandate to build from
scratch, I would have let the basic object model emerge from a detailed
analysis of the probelm  domain)

However, I am uncomfortable with this approach. This is allowing the API to drive the OO design. Design the service functionality for the original C /requirements/ first (i.e., ignoring the actual C implementation). Then figure out how to implement the Facade to invoke that functionality.



3) Some of the classes that we identified initially sounded like controller classes ---- that had "or"s and "er"s in their name, and they violated the "er-er" principle. For each such class we asked the question: What does it do, whatever it has to do, to? For example, one of the classes that we "discovered" was Configurator ----- and we asked the question: What does the Configurator configure? The answer was the Execution Environment ---- so we introduced a class named Environment.

That's good.


4) All the global variables were replaced by the singleton design pattern ----- for the most part, each global variable became a singleton instance, but there were instances where a group of global variables got tied to the same singleton instance.

Yechh. Don't Do That!

Put the data in the objects that abstract problem space entities that would logically have that knowledge. Then control access through the relationship participation at the object level. This is a good example of my problem above where you are letting the API drive the OO design.


5) All the API implementations became a single method invocation on an object ---- any "fanout" to other methods on collaborating objects were handled in the top level method only. In other words there was no control logic or "chaining" of object method invocations left in the API implementation. Even though we did not think of it as a big deal initially, it did come in pretty handy when we were asked to capture the new design in the Client's OO tool. On the sequence diagrams we were able to show the buisness logic of the application as an actor and the API function call as the interface entity. We were thus able to clearly delineate between the functional and OO worlds ---- on the "West" side of the interface entity we had the API function call, and on the "East" side of the interface entity we had a method invocation on a class. One of my distignushed colleagues had included some control logic in the API implementation ---- instead of just a single method call invocation on an object. During the review, he got repeatedly thrashed by the question: Which entity provided this functionality?

That's also good, generally speaking. However, there is nothing to prevent the API implementation from making multiple dispatches. It exists to provide a mapping of the client's request into the actual implementation. That may not be 1:1 because of the client expectations. When the mapping is not 1:1, the Facade implementation should "own" the mapping, even if that involves multiple method invocations.


For example, most test hardware (e.g., oscilloscopes, DVMs,) have a basic interface of Setup (the hardware), Initiate (the test), and Fetch (the results). However, the client may want to see a more basic interface like DoIt (the whole test). In that case the semantics of test are agreed (i.e., the client passes the right inputs and gets the expected results) but the syntax of invocation is different. If the service is implemented assuming the finer granularity of interface, then it is logically correct to encapsulate the syntactic "glue" in the Facade interface so that the DoIt interface method invokes the Setup, Initiate, and Fetch methods in sequence on different objects.

[I once dealt with a situation where the syntactic mismatch was so bad that it required 80 KLOC to translate between the client and the service (each of which were MLOC+ systems). The Facade interface turned out to be an entire "buffer" subsystem.]

My point in this last bit is that the Facade may have a significant implementation to provide the "glue" for the different paradigm viewpoints. On the OO side you want the solution to be pristine in the sense that it doesn't care about the world outside. It just wants to deal with its own requirements. So the solution for those requirements should not address anything necessary to "glue" the paradigms together. That "glue" should be encapsulated in the Facade interface implementation.


************* There is nothing wrong with me that could not be cured by a capful of Drano.

H. S. Lahman
hsl@xxxxxxxxxxxxxxxxx
Pathfinder Solutions  -- Put MDA to Work
http://www.pathfindermda.com
blog: http://pathfinderpeople.blogs.com/hslahman
(888)OOA-PATH



.



Relevant Pages

  • Re: [Linux-cluster] Re: GFS, whats remaining
    ... It is just supposed to be an interface for their tools. ... syscall one) and it's feasible to migrate the opentricksies to that API ... access to part of the functionality, but there are other APIs which are ... send the line "unsubscribe linux-kernel" in ...
    (Linux-Kernel)
  • Re: IStream to file
    ... IStream is just an interface, so it doesn't have any functionality. ... (via e.g. the CreateFile API). ...
    (microsoft.public.vc.language)
  • Re: For the AdaOS folks
    ... API that says create/delete an integer? ... >>as an Ada binding, I can do another registry query and get the ... libraries can strengthen the typing of the Ada interface. ...
    (comp.lang.ada)
  • Re: Difference between .NET and Java
    ... > people that I know agree with my speculation that the CLR was in fact ... > similarities between the CLR and the Win32 API. ... Actually what I ment to say was the .NET has Windows specific functionality ...
    (comp.lang.java.programmer)
  • Re: object system...
    ... [A C compiler cannot put a loop control variable in a register without ... and then later may assemble them into ... this whole API may be modified and redirected (in ... functionality it exports, but instead redirects it to the particular library ...
    (comp.object)

Quantcast