Okay, this question has evolved a bit, and I want to try to start (over) with the basic goals I'm shooting for:
- Create library code that wrappers legacy C-language entities in C++ resource acquisition is initialization and also provides basic or better exception guarantee.
- Enable clients of this code to use it in a very natural C++ fashion w/o creating a lot of overhead to existing code to convert it to use the C++ wrapper objects (i.e. automatic conversion to appropriate legacy types, constructors that take legacy types, etc.)
- Limit the namespace impact of the library code. Ideally, The library would have several sub-namespaces that provide related functionality that limit the volume and impact of using namespace X type declarations - much as the boost libraries do (i.e. use of details namespaces to only inject those symbols that the user would reasonably want to use, and hide those that are implementation details; also limit the extent of possible new meanings to existing symbols, to avoid surprising implicit conversions within user-code)
- Require that clients explicitly ask for those parts of the library that they actually want to inject into their code base. This goes hand in hand with limiting the impact of inclusion of the the library's headers. The client code should have a reasonable level of control over which parts of the library are going to be automatically used for name-resolution when they compile their code.
- My own library code should not have to be riddled with refactor-brittle code constructs. It would be ideal if the library's headers didn't have to constantly declare private typedefs in order to have access to the rest of that section of the library. Or in other words: I want my library to be able to be written as intuitively as my clients get to when making use of said library. Name resolution should include the namespace that the library is defined within in addition to any others that have been explicitly "using'd".
I come across this scenario often, and am looking for a better way...
I have a class, C in namespace N. C has a member, Free. It free's something that C manages, and allows C to manage a new thing.
There are several global Free functions. There are also a few helper functions in the same namespace N as C, one of which is a helper that free's the thing managed by C, named free.
So we have something like:
namespace N {
void free(THING * thing);
class C
{
public:
... details omitted...
free()
{
free(m_thing); // <- how best to refer to N::free(THING&)
}
}
} // namespace N
I could use N::free(m_thing). But that seems unfortunate to me. Is there no way to refer to that which is outside the class scope but without resolving absolute namespace (a relative one step out scope-wise)?
It seems to me that having to name N::free is obnoxious, since you wouldn't have to if this were a free-standing function. Nor would you need to if the class's method name happened to be different (e.g. dispose). But because I've used the same name, I cannot access it without having to specify what amounts to an absolute path - rather than a relative path - if you'll indulge me the analogy.
I hate absolute paths. They make moving things around in namespaces very brittle, so code-refactoring becomes much uglier. Plus, the rules of how to name things in function bodies becomes more complex with the current set of rules (as I understand them) - less regular - inducing a schism between what one expects and what one gets as a programmer.
Is there a better way to access free-standing functions in the same namespace as a class without having to absolutely name the free-function absolutely?
EDIT: Perhaps I should have gone with a less abstract example:
namespace Toolbox {
namespace Windows {
// deallocates the given PIDL
void Free(ITEMIDLIST ** ppidl);
class Pidl
{
public:
// create empty
Pidl() : m_pidl(NULL) { }
// create a copy of a given PIDL
explicit Pidl(const ITEMIDLIST * pidl);
// create a PIDL from an IShellFolder
explicit Pidl(IShellFolder * folder);
...
// dispose of the underlying ITEMIDLIST* so we can be free to manage another...
void Free();
};
So ITEMIDLIST* come from a variety of places, and are destroyed with CoTaskMemFree(). I could introduce Pidl as a global name - as well as all of the helper functions in the "Windows Shell.h" header that is part of my toolbox library.
Ideally, I would segment some of the tools in my library by what they relate to - in this case the above all relates to COM programming in Windows. I have chose Toolbox as the base namespace for my libraries stuff, and was currently thinking I'd use Toolbox::Windows for very windows-y functions, classes, etc.
But the C++ namespace and name-resolution rules seem to make this very difficult (hence this question). It makes it very unnatural to create such segmentation of my code - since koenig lookup fails (since ITEMIDLIST is not in my Toolbox::Windows namespace), and I don't have the ability to move it there! Nor should I. The language should be flexible enough, IMO, to both allow for extension libraries such as my Toolbox library to extend other folks code without having to inject my extensions into their namespace (which, in the case of Win32 and the general vast majority of code that exists today, is the GLOBAL NS - which is the whole point of making namespaces in the first place: to avoid global NS crowding / pollution / ambiguity / programming surprises).
So, I come back around to, Is there a better way to do this: Extend existing libraries of code while not polluting their NS with my extensions but still allow for intuitive and useful name resolution as one would expect if my code were in their NS but explicitly introduced by the client of my code (i.e. I don't want to inject my code willy-nilly, but only upon explicit request)?
Another Thought: Perhaps what would satisfy my above criterea would be if I had the following:
using namespace X {
code here...
}
Where I could place such a construct anywhere, including in a header, and I would not have to be concerned about dragging X into my client's code, but I would have the same freedom to write my source code as I would if I were in the root namespace.