0

The question is in the title but I think it deserves some explanation as it can be very unclear :

I must rewrite in C++ an API currently written in C. The parameters taken in the functions can be handles, contained in a structure of structures (of structures)...

It means that, to manipulate a handle, the user of the API must write something like : getHandleValue(struct1.subStruct1.myHandle);

One of my main objectives by rewriting the code in C++ is to implement all of this in Object Oriented style.

So I'd like something like : myObject->getValue; it's also to avoid the tedious calling of the handle with all the structures and sub structures (reminder : struct1.subStruct1.myHandle)

The main issue I encounter is that two handles from two different subStructures can have the same name. Same for the subStructures, two can have the same name in two different structures.

So I have that question:

Is it possible to forget the tedious calling with all the . and make the type of calling I want possible ? if it's not with an object, is it possible with a simple handle(getHandleValue(myHandle)), somehow "hiding" the whole actual address of the handle to the user ?

And in any cases, when you call handle1 for instance, how can you tell you call the handle1 from subStructure1 or the handle1 from subStructure2 ?

bolov
  • 58,757
  • 13
  • 108
  • 182
Saisey
  • 1

2 Answers2

2

If you wanted to make your question more useful for both yourself and others, you'd probably need to tell us a bit more about the problem domain, and what the API is for. As it stands, it's a question whose original form would not be useful to anyone, yourself included, since its narrow scope bypasses everything that you really would like to know but don't know yet that you need to know :) You don't want to make the question too wide in scope, since then it may become off-topic on SO, thus your application-specific details would be needed. I'm sure you could present them in a generic way so that you wouldn't spill any secrets - but we do need to know the "concrete shape" of the problem domain whose API you'd be reimplementing.


It's a trivial task as presented, but it's up to you to decide which handle is actually needed, so if multiple handles have the same name, you have to distinguish between them somehow, e.g. by using different getter method names:

auto MyClass::getBarHandle() const { return foo.bar.h1; }
auto MyClass::getBazHandle() const { return foo.baz.h1; }

Alas, you don't really want the answer to this detail yet - the implementation details have obscured the big picture here, and this is a classical XY problem. I'd be very leery of assuming that the concept of low-level "handles" needs to be captured directly in your C++ API. It may be that iterators, object references and values are all that the user will need - who knows at this point. This has to be a conscious choice, not just parroting the C API.

You're not "porting" an API to C++. There's no such thing. Whoever uses such a term has no idea what they are talking about. You have to design a new API in C++, and then reuse the C code (or even the C API as-is, if needed) to implement it. Thus you need to understand the C++ idioms - how anyone writing C++ expects a C++ API to behave. It should be idiomatic C++. Same could be said of any expressive high level language, e.g. if you wanted to have a Python API, it should be pythonic (meaning: idiomatic Python), and probably far removed from how the C API might look.

Points to consider (and that's necessarily just a fraction of what you need to think about):

  1. iterator support so that your data structures can be traversed - and that must work with range-for, otherwise your API will be universally hated.

  2. useful range/iterator adapters and predicate functions , so that the data can be filtered to answer commonly asked questions without tedium (say you want to iterate over elements that fulfill certain properties).

  3. value semantics support where appropriate, so that you don't prematurely pessimize performance by forcing the users to only store the objects on the heap. Modern C++ is really good at making value types useful, so the "everything is accessed via a pointer" mindset is rather counterproductive.

  4. object and sub-object ownership - this ties into value semantics, too.

  5. appropriate support of both non-modifying and modifying access, i.e. const iterators, const references, potential optimizations implied by non-modifying access, etc.

  6. see whether PIMPL would be helpful as an implementation detail, and if so - does it make sense to leverage it for implicit sharing, while also keeping in mind the pitfalls.

You need to have real use cases in mind - ways to easily accomplish complex tasks using the power of the language and its standard library - so that your API won't be in the way. A good C++ API will not resemble its counterpart C API at all, really, since the level of abstraction expected of C++ APIs is much higher.

implement all of this in Object Oriented style.

The task isn't to write in some bastardized "C with objects" language, since that's not what C++ is all about. In C++, all encapsulated data types are classes, but that doesn't mean much - in C you also would be operating on objects, and a good C API would provide a degree of encapsulation too. The term "object" as it applies to C++ usually means a value of some type, and an integer variable is just as much an object as std::vector variable would be.

It's a task that starts at a high level. And once the big picture is in place, the details needed to fill it in would become self-evident, although this certainly requires experience in C++. C++ APIs designed by fresh converts to C++ are universally terrible unless said converts are mentored to do the right thing or have enough software engineering experience to explore the field and learn quickly. You'd do well to explore various other well-regarded C++ APIs, but this isn't something that can be done in one afternoon, I'm afraid. If your application domain is similar to other products that offer C++ APIs, you may wish to limit your search to that domain, but you're not guaranteed that the APIs will be of high quality, since most commercial offerings lag severely behind the state of the art in C++ API design.

Kuba hasn't forgotten Monica
  • 88,505
  • 13
  • 129
  • 275
0

@Unslander Monica : First, thanks for your fast and dense answer. There's a lot of useful information and some technical terms I didn't know about so thanks very much !

You're not "porting" an API to C++. There's no such thing. Whoever uses such a term has no idea what they are talking about.

I didn't say I was porting the API, I just said that I was rewriting it, doing another version in a different language. And yes, I'm a "fresh convert" as you say but I'm not a complete ignorant. :)

I did do a high level work, for instance I made a class diagram and use cases. I also put myself in a user's shoes and called the API functions the way I'd see it. But, now that it comes to the implementation, I ask myself some questions of feasibility. The question I asked in my publication was more a question of curiosity than a distress call...

Anyway, as you guessed I can't talk much about my project since it's private. But what I can do is give you the big picture

Currently : This is generated automatically from a XML file. We parse it, then create the following type of structure :

struct {
    HANDLE hPage;
    struct {
        HANDLE hLine1;
        struct {
            HANDLE hWord;
        }tLine1;
        HANDLE hLine2;
        struct {
            HANDLE hWord;
        }tLine2;
    }tPage;
}tBook;

The user then calls any object via its handle. For example getValue(tBook.tPage.tLine2.hWord); This is in C. In C++, it won't be structures but classes with a collection of objects defined by me. The class Page will have a collection of Lines for instance.

class Page {
private :
list<Line> lines;
}

The functions available for the user are mostly basic ones (set/get value or state, wait...) The API's job is to call with its functions, several functions from diverse underlying software components.

Concerning your remarks,

Thus you need to understand the C++ idioms - how anyone writing C++ expects a C++ API to behave. It should be idiomatic C++.

I've already thought of ways to introduce RAII, STL lib, smart pointers, overloaded operators... etc

iterator support so that your data structures can be traversed - and that must work with range-for

What do you mean by "range-for" ? Do you mean range-based for loops ?

so the "everything is accessed via a pointer" mindset is rather counterproductive.

That's more the philosophy of the current API in C, not mine :)

The task isn't to write in some bastardized "C with objects" language

No of course. But the current API's functioning is very, very hard to understand and some functions are really dense and sometimes too much complicated to even rewrite them in a different way. For timing constraints, unfortunately I won't be able to adapt all of the API and my first thoughts when I saw the code is "OK... how do I do it in C++ ? In C, it's handles stocked in structures, in C++ it would be classes stocking handles, directly objects ?" Hence me saying "rewrite it Object Oriented style" ;) sorry if that came out wrong

Also you're right about exploring other APIs, that's what I've been doing with Qt framework. And, I lack C++ experience, that's why I come here, maybe I'm missing something simple here, or something I just don't know yet !

I'm here to learn, because I don't want to make a "terrible API", just like you said in your pep talk... ;)

Anyway, I hope that this answer helps you to understand a little more my problem!

Saisey
  • 1