silentorb
silentorb

Reputation: 2592

Different C++ Class Declarations

I'm trying to use a third party C++ library that isn't using namespaces and is causing symbol conflicts. The conflicting symbols are for classes my code isn't utilizing, so I was considering creating custom header files for the third party library where the class declarations only include the public members my code is using, leaving out any members that use the conflicting classes. Basically creating an interface.

I have three questions:

  1. If the compilation to .obj files works, will this technique still cause symbol conflicts when I get to linking?

  2. If that isn't a problem, will the varying class declarations cause problems when linking? For example, does the linker verify that the declaration of a class used by each .obj file has the same number of members?

  3. If neither of those are a problem and I'm able to link the .obj files, will it cause problems when invoking methods? I don't know exactly how C++ works under the hood, but if it uses indexes to point to class methods, and those indexes were different from one .obj file to another, I'm guessing this approach would blow up at runtime.

Upvotes: 5

Views: 453

Answers (4)

Tom Leys
Tom Leys

Reputation: 19029

You can move the entire library into a namespace by using a clever trick to do with imports. All the import directive does is copy the relevant code into the current "translation unit" (a fancy name for the current code). You can take advantage of this as so

I've borrowed heavily from another answer by user JohnB which was later deleted by him.

// my_thirdparty.h
namespace ThirdParty {
  #include "thirdparty.h"
  //... Include all the headers here that you need to use for thirdparty.
}

// my_thirdparty.cpp / .cc 
namespace ThirdParty {
  #include "thirdparty.cpp"
  //... Put all .cpp files in here that are currently in your project
}

Finally, remove all the .cpp files in the third party library from your project. Only compile my_thirdparty.cpp.

Warning: If you include many library files from the single my_thirdparty.cpp this might introduce compiler issues due to interaction between the individual .cpp files. Things such as include namespace or bad define / include directives can cause this. Either resolve or create multiple my_thirdparty.cpp files, splitting the library between them.

Upvotes: 1

Tobias
Tobias

Reputation: 845

I recommend using a wrapper to encapsulate the third party library.

Wrapper.h

#ifndef WRAPPER_H_
#define WRAPPER_H_

#include <memory>
class third_party;

class Wrapper
{
public:
    void wrappedFunction();
    Wrapper();
private:
// A better choice would be a unique_ptr but g++ and clang++ failed to
// compile due to "incomplete type" which is the whole point
        std::shared_ptr<third_party> wrapped;
};
#endif

Wrapper.cpp

#include "Wrapper.h"
#include <third_party.h>

void Wrapper::wrappedFunction()
{
     wrapped->command();
}

Wrapper::Wrapper():wrapped{std::make_shared<third_party>()}
{
}

The reason why a unique_ptr doesn't work is explained here: std::unique_ptr with an incomplete type won't compile

Upvotes: 1

Alexander Oh
Alexander Oh

Reputation: 25641

For explanatory purposes a simplified explaination follows.

c++ allows you to use functions you declare. what you do is putting multiple definitions to a single declaration across multiple translation units. if you expose the class declaration in a header file your compiler sees this in each translation unit, that includes the header file.

Therefore your own class functions have to be defined exactly as they have been declared (same function names same arguments). if the function is not called you are allowed not to define it, because the compiler doesn't know whether it might be defined in another translation unit.

Compilation causes label creation for each defined function(symbol) in the object code. On the other hand a unresolved label is created for each symbol that is referenced to (a call site, a variable use).

So if you follow this rules you should get to the point where your code compiles but fails to link. The linker is the tool that maps defined symbols from each translation-unit to symbol references.

If the object files that are linked together have multiple definitions to the same functions the linker is unable to create an exact match and therefore fails to link.

In practice you most likely want to provide a library and enjoy using your own classes without bothering what your user might define. In spite of the programmer taking extra care to put things into a namespace two users might still choose the same name for a namespace. This will lead to link failures, because the compiler exposed the symbols and is supposed to link them.

gcc has added an attribute to explicitly mark symbols, that should not be exposed to the linker. (called attribute hidden (see this SO question)) This makes it possible to have multiple definitions of a class with the same name. In order for this to work across compilation units, you have to make sure class declarations are not exposed in an interface header as it could cause multiple unmatching declarations.

Upvotes: 1

Keith
Keith

Reputation: 6834

In theory, you need identical declarations for this to work.

In practice, you will definitely need to make sure your declarations contain:

  • All the methods you use
  • All the virtual methods, used or not.
  • All the data members

You need all these in the right order of declaration too.

You might get away with faking the data members, but would need to make sure you put in stubs that had the same size.

If you do not do all this, you will not get the same object layout and even if a link works it will fail badly and quickly at run-time.

If you do this, it still seems risky to me and as a worst case may appear to work but have odd run time failures.

"if it uses indexes ": To some extent exactly how virtual functions work is implementation defined, but typically it does use an index into a virtual function table.

What you might be able to do is to:

  • Take the original headers
  • Keep the full declarations for the classes you use
  • Stub out the classes and declarations you do not use but are referenced by the ones you do.
  • Remove all the types not referenced at all.

Upvotes: 1

Related Questions