GCC: how do I export (selected methods and) the vtable / typeinfo of a class without exporting the whole class?

2 min read 07-10-2024
GCC: how do I export (selected methods and) the vtable / typeinfo of a class without exporting the whole class?


Exporting Vtables and Typeinfo without Exporting the Whole Class in GCC

The Problem

You're working with a large C++ codebase, and you want to expose certain methods of a class to other modules without having to export the entire class itself. This can be especially useful when you want to keep the internal structure of your class hidden from external users, but still allow them to interact with specific functionalities.

However, you run into a roadblock: exporting the methods requires the vtable and typeinfo of the class, which are typically tied to the class's definition. This begs the question: how do you export only the selected methods, along with their associated vtable and typeinfo, without exposing the entire class's definition?

The Scenario

Let's illustrate with a simple example:

// Class.cpp
#include "Class.h"

class Class {
public:
  void publicMethod() { /* ... */ }
  int privateMethod() { /* ... */ }

private:
  int privateData;
};
// Class.h
#ifndef CLASS_H
#define CLASS_H

class Class; // Forward declaration

void usePublicMethod(Class* obj); // Function using publicMethod

#endif

In this scenario, we only want to allow external code to call publicMethod(). The usePublicMethod() function in Class.h illustrates this requirement. However, exporting publicMethod() will also require exporting the vtable and typeinfo for Class, which would inevitably expose the privateMethod() and privateData member.

The Solution: GCC's __attribute__((visibility("hidden")))

GCC provides a powerful attribute, __attribute__((visibility("hidden"))), that allows you to control the visibility of symbols (functions, variables, classes) during linking. By applying this attribute to the class definition, we can hide it from external modules while still exporting the desired methods.

// Class.cpp
#include "Class.h"

__attribute__((visibility("hidden"))) class Class {
public:
  void publicMethod() { /* ... */ }
  int privateMethod() { /* ... */ }

private:
  int privateData;
};

void usePublicMethod(Class* obj) {
  obj->publicMethod();
}

void exportPublicMethod() {
  // Export only the publicMethod symbol
  // This ensures vtable and typeinfo for publicMethod are exported 
  // without exporting the entire class definition.
  __attribute__((visibility("default"))) void (*p)(Class*) = &Class::publicMethod;
}

Here's a breakdown of the solution:

  1. Hide the class: The __attribute__((visibility("hidden"))) attribute is applied to the Class definition. This makes the entire class invisible to external modules.
  2. Export the desired method: We create a function exportPublicMethod() which uses a pointer-to-function to point to Class::publicMethod(). This function is then marked with __attribute__((visibility("default"))), ensuring it is visible externally.
  3. Exporting the Vtable and Typeinfo: By exporting publicMethod() through a pointer-to-function, GCC automatically exports the necessary vtable and typeinfo information required for its proper invocation. This is achieved without exposing the entire Class definition.

Advantages

  • Information Hiding: This approach allows you to effectively hide the internal details of your class from external users, enhancing modularity and encapsulation.
  • Code Reusability: You can reuse the same class definition for different parts of your codebase, each with varying levels of exposure.
  • Reduced Complexity: The code remains cleaner and easier to understand, as you don't have to resort to complex workarounds to achieve selective visibility.

Limitations

  • Compiler-Specific: The __attribute__((visibility("hidden"))) attribute is a GCC-specific feature. Other compilers might have different syntax or functionality for controlling symbol visibility.
  • Dynamic Linking: This approach might not be suitable for scenarios where you need to dynamically load and unload code modules, as the exported symbol might not be correctly resolved.

Conclusion

By leveraging GCC's __attribute__((visibility("hidden"))), you can selectively export methods from a class without exposing the entire class definition. This approach promotes information hiding, code reusability, and overall clarity in your codebase. Remember to consider compiler-specific limitations and explore alternative solutions if needed.