As discussed allocation/deallocation of memory should be left to the same DLL
Especially when different compilers are used. Then you have different memory managers
Do not use name mangling (decoration) this differs per compiler
Interfaces are always pure virtual
Make names in the interface unique, do not use overloading.
I found out by experience that Visual and Borland use different orders!
When Visual would call the first routine in the implementation Borland called the second.
Allways use base types or other interfaces for parameters and return values.
I explicitely use __cdecl for every interface method.
When using enums be sure they have the correct size.
Compilers might make them long or char depending on options and size.
Use a special enum entry to force an enum to become a long and never be optimized into a character.
EX_FORCE_ENUM_TO_BE_INT = 0xEFFFFFFF
Once you created an Interface, and people use it, NEVER change the order of methods.
You can put new methods on the bottom. So your old programs can still use the old calls.
Never change the parameters of methods.
It would be handy to have some version control.
The most simple way would be to have a version() method in the interfaces.
I never said that. But I see no other api having a problem doing it. But I do know that in the end, the end user has to do it anyways, so why force the user to have to create a std::string/wstring and pass const char* to it when you can simply just return it. Doesn't make much sense.
In the interface programming you do not want expose std::string in an interface. This class highly vendor specific. I know for sure that STL from Microsoft and from SGI adheres to standard public functions but implemented in completely different way.
Interface based programming is all about interoperability not just with your modules but also with other people modules. If sizeof of std::string in one module different from sizeof std::string in other module - your interface is toast.
Even if their sizof are equal but the binary layout is different - your interface is toast.
How can binary layout can be different - many things:
* order of declarations of virtual functions,
* extra member variables as well as order of their declarations
* containment of other classes
to name a few. First call with std::string as parameter will crash program on account of gross misalignment.
You technically can wrap std::string in an interface but thats obvious overkill since std::string is sugar coated char buffer. there also no any overhead of "converting" std::string to cont char* because std::string is const char*.
In an interface you would want to expose PODs or other interfaces ONLY if possible to achieve max interoperability.
One word for you, mate. PORTABILITY. Choosing char* (or wchar_t* these days) makes your DLL easily usable by applications written in other languages. Simple types are easily convertible between languages while objects are not.
Consider that your are an employee in company A, that uses an old and immortal app written lets say in Delphi 7. You are a C++ developer and you want to add a new module written in C++ while some dinosaur Delphi 7 programmer is to take care of the coding on the app side. Now how, for the love of Mother Mary and God, do you expect him to handle std::string while char* is simply PChar in Delphi7???
Use a "release" or "dispose" method on each interface. That will eliminate the insanity of keeping a hash map of pointers to every object ever allocated. Construction and destruction just got easier. This way you don't need to figure out which DLL's DeleteClassInstance to call when you want the object gone.
You technically can use the delete operator. But this works only if both DLL and invoking module link to runtime C dynamically. This is very limiting (if you want to avoid side-by-side stuff with libraries post VC2005 and many other cases). If you link statically then you can only delete new'ed pointer inside the DLL that the class was new'ed in.
This is not an insanity. This is a VERY thoughtful design which you should not underestimate. Both host app and DLL can be build using different language/compiler/linker/environment/you name it, that uses different methods of memory management. Using delete would cause nothing but tons of access violation exceptions.
Steve was talking about the delete operator implemented in the class I assume.
You can 'overload' this method and the 'normal' delete would call this 'overloaded' operator so ending up in the interfaced implementation rather than the caller's memory manager.
I prefer a method like release because you know what happens.
Overloading hides everything. I like to be in control
It might even be better to have the object reference counted.
So you can send the object to other routines that might be in other dll's created by other compilers,
that want to take control for a while.
Boost could be used to accomplish ref counted objects but those templates are
compiled differendly per compiler and therefore not usable.