You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@qpid.apache.org by Alan Conway <ac...@redhat.com> on 2009/04/02 16:14:02 UTC
C++: proposal to clean up the public API.
We need to clean up our public C++ API so that we can:
- identify when we might be making incompatible changes that will break
existing user code.
- reduce our implementation exposure to incompatible changes.
Here are some thoughts in that direction, feedback appreciated:
* Public API design principles
** Rationale
Any change to code that can be seen by the user's compiler can potentially break
compatibility.
There are two levels of compatibility:
- Binary compatible: user binaries built against old headers/libs can be linked
with new libs and will work.
- Source compatible: user source code can be re-compiled without change and
still work.
Future-proofing the API means:
- clearly identifying public header files - those that are installed and can be
seen by user's compiler.
- minimizing the amount of code in public header files.
- hiding implementation as much as possible in public header files.
** Design Principles
Public header files live under cpp/include, private header files live under cpp/src
- Only headers in cpp/include are installed and visible to the user's compiler.
QPID_*_EXTERN declaration are required on:
- all member functions intended to be called by users.
- all member functions used by another qpid library (e.g. common lib functions
used by client/broker libs)
See
http://stevehuston.wordpress.com/2009/03/12/lessons-learned-converting-apache-qpid-to-build-dlls-on-windows/
for some discussion on this subject.
Public classes should fall into one of the following 3 categories:
Handle: handle to refcounted object (e.g. Connection, Session)
- pure pointer to impl (PIMPL) idiom: no data except impl pointer, no virtual
functions, no inlines.
- qpid::client::Handle provides common base class
- defined in client lib, namespace ::qpid::client
Interface: abstract base class, intended for user to subclass (e.g. MessageListener)
- defined in client lib, namespace ::qpid::client
Value: data type with value semantics (e.g. std::string, FieldTable)
- defined in common lib, namespace ::qpid
- imported into ::qpid::client namespace with using statements.
- more on value types below, this is the area that needs most work.
Note type system is defined in common lib, ::qpid namespace so it can be shared
by client & broker code.
It is imported into ::qpid::client namespace so a typical client need only use
the qpid::client namespace.
No boost headers included in public .h files: I think this is
feasible. It would the boost-devel requirement for clients and avoid
incompatibilities due to boost version changes.
* Value Types Proposal
Value types have the most exposed implementation so need to be kept as simple
and clean as possible.
They should:
- represent strictly the *users* view of the data type: no encode/decode functions
- have value-semantics - no virtuals, normal copy semantics etc.
- not be tied to a specific AMQP protocol version - this is the C++ view of
the type.
Integral types: typedef int8_t Int8; typedef uint16_6 Uint16; ... etc.
String types: typedef std::string String;
Additional classes: class SequenceNumber; class SequenceSet; class Url etc.
Discriminated union type: needed by FieldTable and Url. Currently Url
uses a boost::variant which is not extensible, and FieldTable uses
FieldValue which has a somewhat ad-hoc interface.
Propose replacing both with class qpid::Any, modelled after boost::any
with some additional features described below. This provides a
standard-ish (boost::any is proposed for a future C++ standard) API
that allows us to store *any* C++ type without modifications.
FieldTable: propose replacing with
typedef std::map<std::string, Any> Map;
Keep deprecated framing::FieldTable for compatibility.
This allows code like:
map["foo"] = std::string("abc"); // Creates an any containing std::string
std::string* s = anyCast<std::string>(&map["bar"]);
if (s) { do string stuff }
uint16_t i = anyCast<uint16_t>(&map["x"]); // throw exception if not a uint16_t
as well as all the standard map iterator stuff.
** Relating C++ types to protocol types.
There is not an exact 1-1 mapping between protocol types and the basic
value types described above. E.g. the protocol's str8, str16, and
str32 all map to std::string.
Where we need a 1-1 mapping (e.g. in a Map) we use **type wrappers** e.g.
namespace qpid::amqp_0_10 {
struct String8 { String value }; // ctors & conversions to & from String
omitted
struct String16 { String value };
struct Vbin32 { String value };
etc.
};
So to insert a value in a map that I want encoded as a string with an 8 bit length:
map["foo"] = amqp::0_10::String8("bar");
An Any decoded off the wire will always use the wrapped form of a type. e.g.
if (anyCast<amqp0_10::String8>(&any)) { ... }
else if (anyCast<amqp0_10::String16>(any)) { ... }
...
To allow the user to avoid these protocol specifics we provide anyConvert():
if (anyConvert<String>(&any)) { ... }
This will work if the any contains any type that can be converted to string.
We use **type traits** to associate protocol-specific type codes with C++ types.
TypeCode<T>::value == type code for type T.
There is a default mapping for unwrapped types with multiple mappings, e.g.
TypeCode<std::String>::value == typecode for String32
Finally we add one more function to get the type code from an any:
anyTypeCode(any) returns the type code for the any.
To support code like:
switch(anyTypeCode(any)) {
case STR8_TYPE: ...
case UINT16_TYPE: ...
}
}
QUESTION: all of the above should probably be in a
qpid::amqp_<version> namespace? Can we rely on the 0-10 codes not to
change meaning in future versions? 1.0 type system is simpler but is
it backwards compatible?
---------------------------------------------------------------------
Apache Qpid - AMQP Messaging Implementation
Project: http://qpid.apache.org
Use/Interact: mailto:dev-subscribe@qpid.apache.org
Re: C++: proposal to clean up the public API.
Posted by Alan Conway <ac...@redhat.com>.
Ted Ross wrote:
> Alan Conway wrote:
>> Public classes should fall into one of the following 3 categories:
>>
>> Handle: handle to refcounted object (e.g. Connection, Session)
>> - pure pointer to impl (PIMPL) idiom: no data except impl pointer, no
>> virtual functions, no inlines.
>> - qpid::client::Handle provides common base class
>> - defined in client lib, namespace ::qpid::client
>>
> Could you go into a little more detail on this pattern? I'm
> particularly interested in the lifecycle of the handle objects and how
> the ref counts are maintained.
>
> Thanks,
>
> -Ted
>
It uses refcounts as per boost::intrusive_ptr, but this is not exposed in the
public header files.
For an example see qpid/client/Subscription.h. The public header includes
qpid/client/Handle.h, Subscription.cpp includes qpid/client/HandlePrivate.h.
This pattern is useful for public API, but it's tedious & error prone to write
all the forwarding functions so I wouldn't recommend it for use on non-API code,
a qpid::RefCounted and boost::intrusive_ptr will do the job there.
---------------------------------------------------------------------
Apache Qpid - AMQP Messaging Implementation
Project: http://qpid.apache.org
Use/Interact: mailto:dev-subscribe@qpid.apache.org
Re: C++: proposal to clean up the public API.
Posted by Ted Ross <tr...@redhat.com>.
Alan Conway wrote:
> Public classes should fall into one of the following 3 categories:
>
> Handle: handle to refcounted object (e.g. Connection, Session)
> - pure pointer to impl (PIMPL) idiom: no data except impl pointer, no
> virtual functions, no inlines.
> - qpid::client::Handle provides common base class
> - defined in client lib, namespace ::qpid::client
>
Could you go into a little more detail on this pattern? I'm
particularly interested in the lifecycle of the handle objects and how
the ref counts are maintained.
Thanks,
-Ted
---------------------------------------------------------------------
Apache Qpid - AMQP Messaging Implementation
Project: http://qpid.apache.org
Use/Interact: mailto:dev-subscribe@qpid.apache.org