Avoiding reinventing the wheel for a public api with binary compatibility in mind

I'm writing a public api for a product which will be bundled as a
precompiled dll or binary and one of my goals is binary compatibility
(if not across major versions, at least across minor). I'd like to
implement the pimpl idiom and reference counting for the handle classes
as appropriate, but I'm worried about all the typical stuff (exception
safety, memory leaks, etc) and I'd rather not reinvent the wheel.
eg.
template<typename T>
class MyClass {
public:
T foo();
private:
boost::shared_ptr

On Wed, Nov 4, 2009 at 5:16 PM, Geoff Hilton
Using boost::shared_ptr would be great, but this means clients of my code would have to have the same version of boost installed that we use, and I imagine this could cause conflicts in the event that they happen to use boost (and a different version of it).
In theory, yes, they must use the same version of boost. In practice, you'll be surprised how resilient shared_ptr is to breaking binary compatibility across boost versions. That said, if you want to be bullet-proof, you can do pimpl the old fashion way: struct foo; foo * create_foo(....); void destroy_foo( foo * ); void use_foo( foo * ); Note that users can still easily use shared_ptr: boost::shared_ptr<foo> x(create_foo(....),destroy_foo); Emil Dotchevski Reverge Studios, Inc. http://www.revergestudios.com/reblog/index.php?n=ReCode

Geoff Hilton wrote:
I'm writing a public api for a product which will be bundled as a precompiled dll or binary and one of my goals is binary compatibility (if not across major versions, at least across minor). I'd like to implement the pimpl idiom and reference counting for the handle classes as appropriate, but I'm worried about all the typical stuff (exception safety, memory leaks, etc) and I'd rather not reinvent the wheel.
eg. template<typename T> class MyClass { public: T foo(); private: boost::shared_ptr
impl; }; Using boost::shared_ptr would be great, but this means clients of my code would have to have the same version of boost installed that we use, and I imagine this could cause conflicts in the event that they happen to use boost (and a different version of it). I'd rather not include more code dependencies than I absolutely have to, especially if they may prove problematic in the future. What do I do? I know I'm not the first in this situation. How often have others opted for this dependency as opposed to furnishing their own alternative that would avoid additional dependencies (at the expense of the benefit of time-tested code)?
One way around this is to include the boost libraries you require in your own distribution with the top-level namespace appropriately renamed. Yes, this is ugly, it means distributing the code and modifying it so that all the namespaces are different. e.g. boost::shared_ptr -> boost_1_38::shared_ptr Within your own code you can use a namespace alias to make it easier to move to new versions of boost as and when required. e.g. namespace geoff_boost = boost_1_38; Then use geoff_boost::* within your own code. You can then move your own code to new versions of boost by changing that single alias. Externally you can provide typedefs to smart_ptr classes. e.g. namespace package { class foo; typedef geoff_boost::smart_ptr<foo> foo_ptr } So clients of the library deal with names like package::foo_ptr and have no idea that behind the scenes you're using a specific version of boost hidden behind a couple of namepsaces... It's a trade-off - you have to do something nasty, but the client gets to see nice names, you get to re-use solid code but you'll have to live with the dirty feeling of modifying the boost headers (a perl script or two, so you can do it automatically for a new version) and re-distributing them. n

On Wed, Nov 4, 2009 at 6:52 PM, Nigel Rantor
Geoff Hilton wrote:
I'm writing a public api for a product which will be bundled as a precompiled dll or binary and one of my goals is binary compatibility (if not across major versions, at least across minor). I'd like to implement the pimpl idiom and reference counting for the handle classes as appropriate, but I'm worried about all the typical stuff (exception safety, memory leaks, etc) and I'd rather not reinvent the wheel.
eg. template<typename T> class MyClass { public: T foo(); private: boost::shared_ptr
impl; }; Using boost::shared_ptr would be great, but this means clients of my code would have to have the same version of boost installed that we use, and I imagine this could cause conflicts in the event that they happen to use boost (and a different version of it). I'd rather not include more code dependencies than I absolutely have to, especially if they may prove problematic in the future. What do I do? I know I'm not the first in this situation. How often have others opted for this dependency as opposed to furnishing their own alternative that would avoid additional dependencies (at the expense of the benefit of time-tested code)?
One way around this is to include the boost libraries you require in your own distribution with the top-level namespace appropriately renamed. Yes, this is ugly, it means distributing the code and modifying it so that all the namespaces are different.
e.g. boost::shared_ptr -> boost_1_38::shared_ptr
Within your own code you can use a namespace alias to make it easier to move to new versions of boost as and when required.
e.g. namespace geoff_boost = boost_1_38;
Then use geoff_boost::* within your own code. You can then move your own code to new versions of boost by changing that single alias.
Externally you can provide typedefs to smart_ptr classes.
e.g. namespace package { class foo; typedef geoff_boost::smart_ptr<foo> foo_ptr }
So clients of the library deal with names like package::foo_ptr and have no idea that behind the scenes you're using a specific version of boost hidden behind a couple of namepsaces...
It's a trade-off - you have to do something nasty, but the client gets to see nice names, you get to re-use solid code but you'll have to live with the dirty feeling of modifying the boost headers (a perl script or two, so you can do it automatically for a new version) and re-distributing them.
The tool "bcp" that comes with boost can take out parts of Boost that you want to include in your projects, and as I recall it can also rename namespaces automatically (either that or it is a feature being worked on).

OvermindDL1 wrote:
The tool "bcp" that comes with boost can take out parts of Boost that you want to include in your projects, and as I recall it can also rename namespaces automatically (either that or it is a feature being worked on).
Groovy. I remember hearing about it but didn't know it could do renaming too. n

OvermindDL1 wrote:
On Wed, Nov 4, 2009 at 6:52 PM, Nigel Rantor
wrote: I'm writing a public api for a product which will be bundled as a precompiled dll or binary and one of my goals is binary compatibility (if not across major versions, at least across minor). I'd like to implement the pimpl idiom and reference counting for the handle classes as appropriate, but I'm worried about all the typical stuff (exception safety, memory leaks, etc) and I'd rather not reinvent the wheel.
eg. template<typename T> class MyClass { public: T foo(); private: boost::shared_ptr
impl; }; Using boost::shared_ptr would be great, but this means clients of my code would have to have the same version of boost installed that we use, and I imagine this could cause conflicts in the event that they happen to use boost (and a different version of it). I'd rather not include more code dependencies than I absolutely have to, especially if they may prove problematic in the future. What do I do? I know I'm not the first in this situation. How often have others opted for this dependency as opposed to furnishing their own alternative that would avoid additional dependencies (at the expense of the benefit of time-tested code)? One way around this is to include the boost libraries you require in your own distribution with the top-level namespace appropriately renamed. Yes,
Geoff Hilton wrote: this is ugly, it means distributing the code and modifying it so that all the namespaces are different.
e.g. boost::shared_ptr -> boost_1_38::shared_ptr
Within your own code you can use a namespace alias to make it easier to move to new versions of boost as and when required.
e.g. namespace geoff_boost = boost_1_38;
Then use geoff_boost::* within your own code. You can then move your own code to new versions of boost by changing that single alias.
Externally you can provide typedefs to smart_ptr classes.
e.g. namespace package { class foo; typedef geoff_boost::smart_ptr<foo> foo_ptr }
So clients of the library deal with names like package::foo_ptr and have no idea that behind the scenes you're using a specific version of boost hidden behind a couple of namepsaces...
It's a trade-off - you have to do something nasty, but the client gets to see nice names, you get to re-use solid code but you'll have to live with the dirty feeling of modifying the boost headers (a perl script or two, so you can do it automatically for a new version) and re-distributing them.
The tool "bcp" that comes with boost can take out parts of Boost that you want to include in your projects, and as I recall it can also rename namespaces automatically (either that or it is a feature being worked on).
bcp sounds interesting but according to http://www.boost.org/doc/libs/1_40_0/tools/bcp/bcp.html (which happens to use boost::shared_ptr as an example), using it would pull in 274 header dependencies.

Geoff Hilton wrote:
bcp sounds interesting but according to http://www.boost.org/doc/libs/1_40_0/tools/bcp/bcp.html (which happens to use boost::shared_ptr as an example), using it would pull in 274 header dependencies.
I nearly replied to this yesterday. I'm wondering what you mean here. Are you saying: a) Wow, that's a lot isn't it? Did I do something wrong? b) Oh, cool. But look, it grabbed a lot of stuff. c) Other I don't have access right now to my dev box so I have no idea how many dependencies you should expect to see being pulled in. Regards, n

On Sat, Nov 7, 2009 at 7:13 PM, Nigel Rantor
Geoff Hilton wrote:
bcp sounds interesting but according to http://www.boost.org/doc/libs/1_40_0/tools/bcp/bcp.html (which happens to use boost::shared_ptr as an example), using it would pull in 274 header dependencies. I'm wondering what you mean here. Are you saying: a) Wow, that's a lot isn't it? Did I do something wrong? b) Oh, cool. But look, it grabbed a lot of stuff. c) Other I don't have access right now to my dev box so I have no idea how many dependencies you should expect to see being pulled in.
If there is evidence that a boost library (or even header) includes things that it shouldn't, a ticket should be filed. However note that this has nothing to do with the number of ingluded header files. Emil Dotchevski Reverge Studios, Inc. http://www.revergestudios.com/reblog/index.php?n=ReCode

On Wed, Nov 4, 2009 at 8:16 PM, Geoff Hilton
I'm writing a public api for a product which will be bundled as a precompiled dll or binary and one of my goals is binary compatibility
eg. template<typename T> class MyClass { public: T foo(); private: boost::shared_ptr
impl; };
Use boost::intrusive_ptr<> and make sure the intrusive_ptr_add_ref(T) and intrusive_ptr_release(T) are implemented _inside_ your DLL. Tony

On Wed, Nov 4, 2009 at 11:10 PM, Gottlob Frege
On Wed, Nov 4, 2009 at 8:16 PM, Geoff Hilton
wrote: I'm writing a public api for a product which will be bundled as a precompiled dll or binary and one of my goals is binary compatibility template<typename T> class MyClass { public: T foo(); private: boost::shared_ptr
impl; }; Use boost::intrusive_ptr<> and make sure the intrusive_ptr_add_ref(T) and intrusive_ptr_release(T) are implemented _inside_ your DLL.
That is about as stable binary interface as boost::shared_ptr. Emil Dotchevski Reverge Studios, Inc. http://www.revergestudios.com/reblog/index.php?n=ReCode

On Thu, Nov 5, 2009 at 2:11 AM, Emil Dotchevski
Use boost::intrusive_ptr<> and make sure the intrusive_ptr_add_ref(T) and intrusive_ptr_release(T) are implemented _inside_ your DLL.
That is about as stable binary interface as boost::shared_ptr.
I can see shared_ptr internals changing, for example to make it more threadsafe. I can't see intrusive_ptr changing - by its very nature its internals are, well, external. Tony
participants (5)
-
Emil Dotchevski
-
Geoff Hilton
-
Gottlob Frege
-
Nigel Rantor
-
OvermindDL1