I needed to build up a public API for an application at hand.
The data I am modelling maps very well to object oriented design, so I
am little inclined towards a C++ public API (i.e. my deliverables
would be a <xyz>.hpp file which will have a bunch of pure virtual
functions forming the interface and a lib<xyz>.so which will basically
contain the implementation for those interfaces).
But, I can think of 2 problems there:
1) How do I guarantee that my shared library will work well with the
version of the C++ library/compiler my customer/user is having?
Issue in detail: I mean, I compile my shared library with version 'X'
of a Std CPP compiler and customer tries to use with version 'Y' of
StdCPP compiler. And then, if 'X' and 'Y' versions of CPP compiler are
not compatible, then we get into trouble. We have hit this several
times with different gcc versions. Just wondering, what is the best/
cleanest way to solve/get-around this problem?
2) Sometimes it happens that you are working on a C++ based product
which already has millions of lines of code and then, one fine day a
requirement comes that a part of that chunk needs to be made accesible
to users through a shared library. Now, which you work towards
building that shared library, you may want to ensure that only symbols
that *should be* exported to users/customers, needs to be exported in
the shared library, all others un-wanted globals should not be
exported in the shared library to ensure we don't un-necessarily
pollute customer's namesapce with whole lot of our internal/global
symbols. (Note: Ideal solution for this issue, probably would be to
ensure that we should never pollute our implementation namespace at
all, in the first place, but as I said, you may not be the one who had
written all these code and it's a million lines code, and you don't
have the option/bugdet for re-arch/re-write!). For such a requriement,
if it was a C library we could typically use 'objcopy' in linux or -M
compiler option in Solaris, to make sure only a given set of symbols
(defined by us) are made global. But if it's a C++ library, I wonder
how to achieve this goal?
Issues: C++ symbols are mangled, so I would probably need to specify
those mangled names as an input to 'objcopy', but mangling varies from
compiler to compiler and platform to platform. With that this can
become a real maintenance headache.
Question: Is there a cleaner/better way to handle this?
Thanks in advance for oyour time and help!
Arijit
> I needed to build up a public API for an application at hand.
You cannot do any of this by guessing or planning. To create a public API, you
must write at least three projects, and port them to three different platforms
(linux, mac & pc) come to mind. Then you must configure your unit tests to run
simultaneously on each of the three platforms, each time you save your code. (I
have done this before, between Linux & PC, using Samba. Both platforms compiled
simultaneously into the same folders.)
Your nine projects should run all their tests each time you edit any of their
sources - both inside and outside the API boundary. And you write three
different projects to prove that your API actually solves problems for them,
flexibly.
> The data I am modelling maps very well to object oriented design, so I
> am little inclined towards a C++ public API (i.e. my deliverables
> would be a <xyz>.hpp file which will have a bunch of pure virtual
> functions forming the interface and a lib<xyz>.so which will basically
> contain the implementation for those interfaces).
That's not what "pure virtual" means. It means the methods _don't_ have
implementations. If you go this route, you will simply need virtual methods.
However...
Why do you think the project will need an OO design? The point of OO is to
abstract and vary virtual methods behind common interfaces. Have you written any
sample code showing the best places for the virtual methods? They might not be
what you expect.
You might be safer - if your client actually _needs_ all these platforms - by
writing a C-style API. The C++ communities widely support this technique,
because most platforms enforce compatibility standards among simple functions.
By contrast, because C++ virtual methods must be very optimal, they enjoy fewer
standards. You might not be able to ship binaries, for example, and you might
have to ship live source code for your clients to compile.
> But, I can think of 2 problems there:
> 1) How do I guarantee that my shared library will work well with the
> version of the C++ library/compiler my customer/user is having?
By actually getting all the versions, like I said, and constantly testing them
as you change the code. If you make a change that you think is innocent, and if
one platform throws up a red flag, you can back out the change long before you
commit to it and invest more code around it.
> Issue in detail: I mean, I compile my shared library with version 'X'
> of a Std CPP compiler and customer tries to use with version 'Y' of
> StdCPP compiler. And then, if 'X' and 'Y' versions of CPP compiler are
> not compatible, then we get into trouble. We have hit this several
> times with different gcc versions. Just wondering, what is the best/
> cleanest way to solve/get-around this problem?
Have you surveyed your user population? Can you set up virtual Linuces with each
of these versions?
> Can you write your public API in the most general standards conforming
> way(no platform specific calls)? If so, great! Do so!
That answers the wrong question. The API has two interfaces, one
programmer-facing and the other hardware-facing. The question was about keeping
the upper layer clean and portable.
Almost no program can do anything without platform specific calls - even tasks
as mundane as file reconnaissance are platform-specific. The OP is advised to
get into some full-featured portable platform, such as Qt or wxWindows, to get a
kit of all of those functions, readily ported and supported to a wide variety of
platforms. (Those platforms also come with nice GUI layers, which one can
exploit or ignore.)
> Without even reading the body or you post, I suggest you consider "C++ Guru"
> and expand your question to include the out of the box thinkers, for your
> question is a design one rather than a language-specific one, at least to
> some large degree,
this is about the sanest of Tony' recent posts. This probably *is* a
design
issue rather specifically a C++ problem. It certainly needs to deal
with
issues outside the C++ language.
but then...
> I think. When I think of "C++ Guru", I think of those
> heavily knowledgeable of the C++ standard, who indeed are valuable (if not
> temporary) walking/talking encyclonairies.
... the great Word Salad came upon him and he was <snipped>
--
Nick Keighley
On 31 May, 22:19, ariji...@gmail.com wrote:
> I needed to build up a public API for an application at hand.
>
> The data I am modelling maps very well to object oriented design, so I
> am little inclined towards a C++ public API (i.e. my deliverables
> would be a <xyz>.hpp file which will have a bunch of pure virtual
> functions forming the interface and a lib<xyz>.so which will basically
> contain the implementation for those interfaces).
as another poster pointed out your functions aren't virtual.
Do your APIs contain classes?
> But, I can think of 2 problems there:
> 1) How do I guarantee that my shared library will work well with the
> version of the C++ library/compiler my customer/user is having?
> Issue in detail: I mean, I compile my shared library with version 'X'
> of a Std CPP compiler and customer tries to use with version 'Y' of
> StdCPP compiler. And then, if 'X' and 'Y' versions of CPP compiler are
> not compatible, then we get into trouble. We have hit this several
> times with different gcc versions. Just wondering, what is the best/
> cleanest way to solve/get-around this problem?
it's probably insoluable in *any* language. Might it be better
to define an interface in terms of simple structures that are defined
as streams of bytes. Then provide some sort of RPC interface.
You might then look at ASN.1 or XML.
You loose the tight coupling between your application and the client
but gain the huge advantage of loosing the tight...
<snip>
--
Nick Keighley
Write a C wrapper. If your interface were
class Interface {
public:
virtual int f( double );
};
Interface* new_foo();
you could write a C wrapper as
typedef struct Interface Interface;
Interface* xyz_new_foo( void );
void xyz_delete( Interface* );
int xyz_f( Interface*, double );
and it would be accessible from many languages. If you then wanted C++
users to have a more convenient interface, you could provide a client-side
wrapper for the C interface. :)
> 2) Sometimes it happens that you are working on a C++ based product
> which already has millions of lines of code and then, one fine day a
> requirement comes that a part of that chunk needs to be made accesible
> to users through a shared library. Now, which you work towards
> building that shared library, you may want to ensure that only symbols
> that *should be* exported to users/customers, needs to be exported in
> the shared library, all others un-wanted globals should not be
> exported in the shared library to ensure we don't un-necessarily
> pollute customer's namesapce with whole lot of our internal/global
> symbols. (Note: Ideal solution for this issue, probably would be to
> ensure that we should never pollute our implementation namespace at
> all, in the first place, but as I said, you may not be the one who had
> written all these code and it's a million lines code, and you don't
> have the option/bugdet for re-arch/re-write!). For such a requriement,
> if it was a C library we could typically use 'objcopy' in linux or -M
> compiler option in Solaris, to make sure only a given set of symbols
> (defined by us) are made global. But if it's a C++ library, I wonder
> how to achieve this goal?
[...]
Write a C wrapper.
> I needed to build up a public API for an application at hand.
>
> The data I am modelling maps very well to object oriented design, so I
> am little inclined towards a C++ public API (i.e. my deliverables
> would be a <xyz>.hpp file which will have a bunch of pure virtual
> functions forming the interface and a lib<xyz>.so which will basically
> contain the implementation for those interfaces).
>
> But, I can think of 2 problems there:
> 1) How do I guarantee that my shared library will work well with the
> version of the C++ library/compiler my customer/user is having?
> Issue in detail: I mean, I compile my shared library with version 'X'
> of a Std CPP compiler and customer tries to use with version 'Y' of
> StdCPP compiler. And then, if 'X' and 'Y' versions of CPP compiler are
> not compatible, then we get into trouble. We have hit this several
> times with different gcc versions. Just wondering, what is the best/
> cleanest way to solve/get-around this problem?
If you ship a C++ interface, you will have to provide a different
binary for every different version of OS and compiler your clients
use. This is because C++ ABI differs across compilers and even
compiler versions (e.g. gcc3 vs gcc4, sunCC vs. sunCC -
library=stlport4).
On the other hand, if you ship a C interface, you will only need to
provide different binaries for each OS. This is because C ABI is
stable and your clients can link against your C API using any compiler
for that particular OS.
Note, that you can do OO design in C. For example, a C++ intreface:
struct Foo
{
int doSomething(int);
};
Would look like this in C:
struct Foo; /* incomplete in the public API */
Foo* fooCreate();
void fooDestroy(Foo*);
int fooDoSomething(Foo*, int);
> 2) Sometimes it happens that you are working on a C++ based product
> which already has millions of lines of code and then, one fine day a
> requirement comes that a part of that chunk needs to be made accesible
> to users through a shared library. Now, which you work towards
> building that shared library, you may want to ensure that only symbols
> that *should be* exported to users/customers, needs to be exported in
> the shared library, all others un-wanted globals should not be
> exported in the shared library to ensure we don't un-necessarily
> pollute customer's namesapce with whole lot of our internal/global
> symbols. (Note: Ideal solution for this issue, probably would be to
> ensure that we should never pollute our implementation namespace at
> all, in the first place, but as I said, you may not be the one who had
> written all these code and it's a million lines code, and you don't
> have the option/bugdet for re-arch/re-write!). For such a requriement,
> if it was a C library we could typically use 'objcopy' in linux or -M
> compiler option in Solaris, to make sure only a given set of symbols
> (defined by us) are made global. But if it's a C++ library, I wonder
> how to achieve this goal?
By exposing a C interface, so that your .so only exports the functions
of the public C API, all other symbols are made local or stripped.
(Don't forget to statically prelink your .so with a C++ run-time, so
that your C API .so does not have any unresolved C++ run-time
symbols).
--
Max
<snip>
>
> Question: Is there a cleaner/better way to handle this?
>
> Thanks in advance for oyour time and help!
>
well, as a few others have said, and I am inclined to agree, my advice is
this:
make the external API be a C-based API.
then, it does not so much matter if the library is written in C, C++, or
some other language.
similarly, it will not matter if the client is written in C, C++, or some
other language (remember, not everyone uses C++, or is inclined to be bound
to using C++ for sake of a library...).
I will take it a little further, namely, the API should be an "abstract"
API, which in this context basically means:
no directly shared data structures (not as difficult, given it would likely
be C++ classes and a C-based API internally, but just as a point of
emphasis: it is rarely a good idea to have shared structures across API
boundaries);
instead, one can use handles, which are typically either integers or opaque
pointers (often "typedef void *myHandleType;" or similar...);
operations and mutations can still be performed, but typically this is via
some collection of abstract wrapper functions;
any data shared should be passed in buffers, and where ideally these buffers
should be no more complex than that of flat arrays (int, float, ...);
should complex data need to be passed, a personal recomendation is to use a
textual, or some other "cannonical" (as in, an established format)
serialization;
...
the reasons for the above are subtle, but important:
directly sharing data and structures between the client and the library is a
good way to make a mess;
in particular, it can tend to cause the client and the library to become
interdependent, and may often lead to minor changes to one side breaking the
other.
after learning this one from experience a few times over, I had adopted
this, arguably overly-strict seeming position (yes, shared data may seem to
make the API more "friendly" or allow tighter coupling, but very often, this
is more of an enemy than a friend...).
as for the buffer rule:
this is similar, and often it turns out this way in practice;
the more complex the passed data is, the more likely it is to need to be
changed later;
textual serializations partly sidestep this rule, as for a given data
complexity a textual format will almost invariably be drastically more
"generic" than an equivalent binary format (it is the case, don't expect me
to explain it, it relies on some esoteric properties...);
a "cannonical" format may also be an option, where this is usually a common
fileformat established for some specific use, for which it may make sense to
pass in a buffer (examples: JPEG, PNG, COFF, ELF, ...).
note (further justification of textual serialization):
it may seem like such a serialization would contribute a good deal of
overhead, however this has not been my experience in practice;
it can be noted that, in most cases where it is necessary to send complex
data, there is also typically a good deal of processing involved, which
would by far dwarf any real (noticable) cost of serializing the text, and
parsing it again;
it can be further noted that, in some cases, specially-crafted text formats
(and with specialized processing code), can match or exceed the raw
performance than could have been achieved via an equivalent binary format
(the key is that not all text needs to be "parsed", and infact, in many
cases it is possible to make what is, essentially, an ASCII-based binary
format...). similarly, although typically cryptic, there is an advantage
that a person familiar with the serialization can directly "read" the data
unaided (whereas a pure-binary format will almost invariably require a
hex-dump), which is particularly helpful with debugging.
granted though, this latter case is not to always be pursued, as it does not
scale so well to larger-scale complexities (at which point it may impose
similar problems to a plain binary format), however, often-times a good
portion of an otherwise more "generic" textual serialization can make good
use of individual components structured in this way (use with discretion,
however...).
note that I am not recommending that everything use XML or somesuch, as for
most tasks, this would be highly overkill, rather, usually a specialized
token-based format is sufficient.
and so on...
FWIW, I will add more:
given C has a single massive shared toplevel, it makes sense to "prefix"
function and type names with some prefix consistent to the library, and
hopefully unlikely to clash with any other library.
a personal style I use is:
libprefixCamelCase(...);
this style being (more or less) reserved for external API functions in my
case.
but, where internally I typically use:
LIBNAME_SubSys_CamelCase(...);
or just:
LIBNAME_CamelCase(...); //this more for smaller, more single-purpose, libs
similarly, in C++-based libraries, namespaces are a good option as well (for
internal use).
keeping this distinction may also better help one keep in mind what is
internal, and what is intended to be part of the external API.
I will also recommend that one actually put in effort and "design" the API,
whereas personally I don't feel as strongly about design WRT the internal
workings of a library (where, often, one may need to change and
rework/redesign the internals maybe numerous times before one has them "just
right", and so personally I would not recommend making the internals so much
"set in stone", whereas the external API is something which should hopefully
change sparingly, if at all...).
or such...
This
> If you then wanted C++
> users to have a more convenient interface, you could provide a client-side
> wrapper for the C interface. :)
and this. Specifically, provide a small wrapper class in a header only
that implements itself in terms of the C API, and give this header to
clients. The class will be compiled by the client's compiler, and all
calls to your library will go through a stable C API. You gain the
stability of the C API and a nice C++ interface class.
Note that it's not always convenient, reasonable or practical to do it
this way, but many times it is.
> Write a C wrapper.
I second to that. C++ has many advantages over C, but binary
interoperability is not one of them. Use C++ interfaces only when both
parties are under your control and you can recompile them easily together.
Paavo
Not just binary, but conceptual interoperability. Almost all languages
have some means of calling C functions, and perhaps even passing and
returning structures. Lots of useful libraries can be presented in this
simple vocabulary. The dividing line between those that work well with a
C interface and those that need a C++ one is probably between libraries
that mostly provide data processing functions (like compression, media
file decoding) and rich data structures (like the STL, or most of boost).
yes, I will add something here:
I have noticed a similar effect before (although, granted, C++ is not my
primary development language), where many APIs seem best served by a clean
(or, almost sterile) and opaque API; yet others consist almost purely of
utility code, which need not have the same design.
for example, the API design which would work well on, say, a physics engine,
would not be the same as the one for, say, ones' geometric-math library
(even if they may typically be used in close relation to each other...).
so, yeah, the best design likely depends a lot on what the library does...
then of course, we have some people who could be paraphrased as "you must
all bow before the greatness of OO"...
What you claim is provably false. What if it's a Windows-specific API?
Also, there is still no law which forces programmers to have unit tests.
Maybe what you mean is that it's hard to invent, implement and
maintain APIs, because you need to predict the needs of the users, and
then pretend to be the user when testing the API. That I would agree
with, strongly.
...
>> The data I am modelling maps very well to object oriented design, so I
>> am little inclined towards a C++ public API (i.e. my deliverables
>> would be a <xyz>.hpp file which will have a bunch of pure virtual
>> functions forming the interface and a lib<xyz>.so which will basically
>> contain the implementation for those interfaces).
>
> That's not what "pure virtual" means. It means the methods _don't_ have
> implementations. If you go this route, you will simply need virtual methods.
> However...
>
> Why do you think the project will need an OO design? The point of OO is to
> abstract and vary virtual methods behind common interfaces. Have you written any
> sample code showing the best places for the virtual methods? They might not be
> what you expect.
OO doesn't mean run-time polymorphism to everyone. Maybe he just means
that an API with classes seems like a good fit. (IIRC, Stroustrup
calls that an "object-based design".)
But that doesn't explain why he wants things virtual. Just to make it
clear: you do *not* need 'virtual' to distribute shared libraries, or
to distribute them in binary form only.
> You might be safer - if your client actually _needs_ all these platforms - by
> writing a C-style API. The C++ communities widely support this technique,
> because most platforms enforce compatibility standards among simple functions.
Yeah, I vote for a C API too. They are widely understood, widely used,
and all serious languages can interface to them. (By all means, write
a C++ wrapper for it, too -- maybe the customer will use it, or maybe
you'll find weaknesses in the C API while doing it.)
/Jorgen
--
// Jorgen Grahn <grahn@ Ph'nglui mglw'nafh Cthulhu
\X/ snipabacken.se> R'lyeh wgah'nagl fhtagn!