bugThe FreeType Project - Bugs: bug #45676, enclose FreeType in a C++ namespace

 
 

You are not allowed to post comments on this tracker with your current authentication level.

bug #45676: enclose FreeType in a C++ namespace

Submitter:  Werner LEMBERG <wl>
Submitted:  Sun 02 Aug 2015 11:12:59 AM UTC
Votes: 1
 
Severity:  3 - Normal Item Group:  Wishlist
Status:  None Privacy:  Public
Assigned to:  None Open/Closed:  Open
Planned Release:  None

Jump to the original submission

Fri 24 Nov 2017 03:01:55 PM UTC, comment #13: 

I just wanted to add that these types of link errors have a high cost as we usually find them late in production and the workflow to fix them is cumbersome. We have patched our quarantine file a couple of times by now and we're looking forward to have a better way to handle this.

If there are any updates on this, or anyone else experiencing the same issues, do let us know.

Mihai Popescu <mihai>
Thu 23 Jun 2016 08:08:09 PM UTC, comment #12: 

Yes. I reached the same conclusion. I should've let you know sooner but I started investigating libpng configuration scripts that handle setting up the library prefix and somehow got lost in them (I'm not an expert in autotools).

If you are going to work on this, please note also that we had to do some extra patching for 2.6.1 in order for this to work. The FT_MulFix preprocessor define/undef from ftcalc .h and ftcalc.c need to be quarantined as well.

Probably the easiest thing to do from the quarantine script would be to do a replace-all-occurrences on all files with the quarantine version of the define.

Mihai Popescu <mihai>
Mon 02 May 2016 06:43:21 AM UTC, comment #11: 

I thought more about this issue, and now I believe that using `FT_S(foo)' (or probably even shortened to `_(foo)' instead of `foo') is indeed a too offensive change since it would be necessary to apply it to all source code files, everywhere, making the code really, really ugly.

Looking up the various references you've given, I now believe that libpng's approach is the one what we should follow if a symbol prefix is requested.

1. Compile a static version of the library.

2. Use `nm' to automatically extract the symbols and construct a header file from it.  This should be also part of the tarball so that platforms without `nm' succeed in compilation, omitting steps 1 and 2 (in other words, those two steps are only performed during a direct build from the git repository).

3. Compile the library again, using the just created header file to rename all global symbols (both functions and static data structures).

Werner LEMBERG <wl>
Group administrator
Wed 25 Nov 2015 08:41:31 AM UTC, comment #10: 

Thanks for the research!

I prefer the solution you've outlined in comment #4, perhaps using `FT_S' instead of the longer `FT_SYMBOL'.

Note that any exported symbols not tagged with FT_EXPORT are a bug.

So please feel free to send patches :)

Werner LEMBERG <wl>
Group administrator
Thu 19 Nov 2015 12:25:49 AM UTC, comment #9: 

Hi Werner,

Getting back to this issue I think the symbol dumping solution would add a dependency on the compiler tools (like nm) and be a portability issue.

Searching for other well-known libraries confronted with the same issue I've found that libpng is using the following approach:

```
Starting with libpng-1.6.0, you can configure libpng (when using the
"configure" script) to prefix all exported symbols by means of the
configuration option "--with-libpng-prefix=FOO_", where FOO_ can be any
string beginning with a letter and containing only uppercase
and lowercase letters, digits, and the underscore (i.e., a C language
identifier).  This creates a set of macros in pnglibconf.h, so this is
transparent to applications; their function calls get transformed by
the macros to use the modified names.
```

The build scripts generate the prefixed symbols controlled with the PNG_EXPORT.

Also in this case, there will be a dependency on the build system used by freetype which is not guaranteed to be the same as in the end-user case (most probably).

zlib also uses a similar (but weaker) approach with a compiler define which just adds a predefined prefix to their exported symbols.

/*

  • If you really need a unique prefix for all types and library functions,
  • compile with -DZ_PREFIX. The "standard" zlib should be compiled without it.
  • Even better than compiling with -DZ_PREFIX would be to use configure to set
  • this permanently in zconf.h using "./configure --zprefix".

 */

It works in their case as is has fewer symbols than libpng.

For FreeType we could take libpng approach and modify FT_EXPORT but it would imply a massive refactoring. Unfortunately I think there are other symbols that are exported not explicitly through FT_EXPORT (as ft_smooth_renderer_class_pic_free for instance).

I'm unsure what approach is the best one for FT but let's discuss.

Thank you,
Mihai

Mihai Popescu <mihai>
Mon 02 Nov 2015 03:05:17 PM UTC, comment #8: 

I would consider using clang refactoring tool. I'm a bit unsure though if it knows how to properly handle macros (as I've seen that it works on preprocessed sources). I'll continue to investigate.

Mihai Popescu <mihai>
Wed 28 Oct 2015 09:52:48 AM UTC, comment #7: 

Graham, what do you think of the suggested FT_SYMBOL solution?  This should be generic enough to be used it with C++ also...

Werner LEMBERG <wl>
Group administrator
Tue 27 Oct 2015 04:15:34 PM UTC, comment #6: 

Hello,


I think this approach is been used in many libraries but I don't know any well-known library that uses this approach. What I can say about this is that this seems to be the recommended approach for this problem. Here are my research findings:

In the "Interface and API Design" Chapter from "Effective Objective C 2.0" the authors recommend to "Use Prefix Names to Avoid Namespace Clashes". [1]

Angel G. Olloqui is arguing the same things in his blog [2] saying "Using prefixes is a good practice that you should always follow in programming languages without namespaces like C or Objective-C."

On the other hand, other approaches like namespaces with structs come with a lot of trade offs as we can read from Edmund Horner's blog post [3].

So basically, prefixing the symbols is the way to do it.

Ryan Arana explains in his blog post [4] a way to automatize the process dumping the library symbols with nm and generating the quarantine header file automatically. Of course, this will make the build process in two phases. The idea seems to be have originated from featherless [5].


References:
[1] https://books.google.dk/books?id=qp4ZXAmGaoQC&pg=PA73#v=onepage&q&f=false
[2] http://angelolloqui.com/blog/31-How-to-fix-a-Duplicated-Symbols-error-on-binary-files
[3] https://ejrh.wordpress.com/2012/01/24/namespaces-in-c/
[4] http://pdx.esri.com/blog/2013/12/13/namespacing-dependencies/
[5] http://stackoverflow.com/questions/11512291/prefix-static-library-ios/19341366#19341366

Mihai Popescu <mihai>
Mon 26 Oct 2015 02:50:36 PM UTC, comment #5: 

Your suggestion looks nice, and I could live with it, thanks – provided you take the time and send a patch...

On the other hand: Are there other, well-known libraries that try something similar?

Werner LEMBERG <wl>
Group administrator
Fri 23 Oct 2015 01:01:32 PM UTC, comment #4: 

@wl: Wrapping into the C++ namespace doesn't solves the issue if you want to compile freetype with C compilers.

To set the context of our "fix" is that we had this issue when we were (dynamically) linking against other libraries with their own FT symbols on Tizen.

All those defines are in a quarantine file which is included in ftbuild.h before config/ftheader as you said.

The problem with this approach is that it's not maintainable and prone to error.

I agree that a better approach would be to use C++ namespace but what about a pure C solution ?

But what about something like this:

---

#ifndef FT_SYMBOL
#define FT_SYMBOL(x) x
#endif

...

  FT_EXPORT( FT_Fixed )
  FT_SYMBOL( FT_Cos )( FT_Angle  angle );

...

But this will imply a massive change in the FT code base. Thoughts?

Mihai Popescu <mihai>
Wed 21 Oct 2015 07:15:42 PM UTC, comment #3: 

Well, it's not about `switching over' but simply wrapping FreeType into a separate namespace while compiling the C library with a C++ compiler.  There won't be any incompatibilities with (older) C compilers.

A user can quite easily customize `ft2build.h', adding the macros you propose.  However, this is rather a C than a C++ solution.  Additionally, no special support within FreeType is necessary for your suggestion.

Werner LEMBERG <wl>
Group administrator
Tue 20 Oct 2015 12:30:54 PM UTC, comment #2: 

To avoid switching over C++ and break compatibility with old C99 compilers, the FreeType2 library functions linker conflicts can be quarantined with #define which can be included from ft2build.h before ftheader.h.

...
#define FT_Activate_Size UNITY_FT_Activate_Size
#define FT_Add_Default_Modules UNITY_FT_Add_Default_Modules
#define FT_Add_Module UNITY_FT_Add_Module
#define FT_Alloc UNITY_FT_Alloc
#define FT_Angle_Diff UNITY_FT_Angle_Diff
#define FT_Atan2 UNITY_FT_Atan2
...

What do you think about this approach instead?

Mihai Popescu <mihai>
Tue 04 Aug 2015 12:19:06 PM UTC, comment #1: 

I have already successfully enclosed FreeType in a C++ namespace as part of using it for my product, CartoType; to avoid conflicts with other versions of FT when linking it statically.

Here are my notes:

My product, CartoType, is sometimes supplied as a static C++ library, and lately I've had some problems reported by users who also link to other libraries containing conflicting versions of open-source components, including FreeType, Zlib, Libpng, Libjpeg, Expat, etc.

One possible fix would be to take these components out of CartoType, and allow users to link to their own versions, but that would compromise consistency and maintainability. Therefore my solution has been to move everything into the CartoType namespace; which means compiling the code as C++ and enclosing all declarations and definitions in "namespace CartoType { ... }". Doing this has helped a number of my clients.

It may interest FreeType aficionados if I give a brief overview of how to do this. My description is based on a somewhat older version of FreeType, but I am sure it remains applicable.This is not the same as creating a C++ wrapper for FreeType. It does not change the FreeType API, but puts everything into the C++ namespace, so that, for example the full signature of FT_Init_FreeType becomes

CartoType::FT_Error CartoType::FT_Init_FreeType(CartoType::FT_Library* alibrary)

Obviously any namespace would work here.

Here's the process I followed:

1. Rename all directly compiled .c files to .cpp. These are files that are not #included, but are part of the main project. So ftbase.c, ftinit.c, truetype.c, raster.c, etc., become ftbase.cpp, ftinit.cpp, truetype.cpp, raster.cpp, etc. The renaming is not absolutely necessary (you could just tell your compiler to compile everything as C++) but it makes things a lot easier if you are working on five different platforms, with many different project files and compilers; all the compilers I use infer C or C++ from the file extension.

2. Change FT_LOCAL, etc, as follows:

#define FT_LOCAL( x )      x
#define FT_LOCAL_DEF( x )  x
#define FT_BASE( x )  x
#define FT_BASE_DEF( x )  x
#define FT_EXPORT( x )  x
#define FT_EXPORT_DEF( x )  x
#define FT_EXPORT_VAR( x )  extern  x
#define FT_CALLBACK_DEF( x )  x
#define FT_CALLBACK_TABLE extern
#define FT_CALLBACK_TABLE_DEF extern

That is, various 'extern "C"' and 'static' qualifiers are removed.

3. Make FT_BEGIN_HEADER and FT_END_HEADER into the start and end of the namespace:

#define FT_BEGIN_HEADER namespace CartoType {
#define FT_END_HEADER } // namespace CartoType

For this to work, I had to remove FT_BEGIN_HEADER and FT_END_HEADER from pshalgo.h, because it #includes other headers; while 'extern "C"' can be nested, a nested namespace is a different namespace.

4. Put all indirectly compiled .c files into the namespace. These are the source files which were not renamed to .cpp files, and are #included by them. They are compiled as C++ because they become part of the same compilation unit when #included. For example, the file ttdriver.c acquires 'namespace CartoType {' after all its #include statements, and '} // namespace CartoType' at the end of the file. I had to do that to about 40 files.

5. Fix up some annoyances in ftinit.cpp. The ordinary system for module specification doesn't work, for reasons I am too bored to investigate involving static object initialisation, 'extern' declarations and strict type checking, so I have to declare the modules like this:

extern const FT_Module_Class psaux_module_class;
extern const FT_Module_Class psnames_module_class;
extern const FT_Module_Class pshinter_module_class;
extern const FT_Renderer_Class ft_raster1_renderer_class;
extern const FT_Module_Class sfnt_module_class;
extern const FT_Renderer_Class ft_smooth_renderer_class;
extern const FT_Driver_ClassRec tt_driver_class;
extern const FT_Driver_ClassRec t1_driver_class;

#define FT_USE_MODULE( x )  (const FT_Module_Class*)&x,

  const FT_Module_Class*  const ft_default_modules[] =
  {
#include FT_CONFIG_MODULES_H
    0
  };

6. There were some other small problems - pretty obvious stuff - but I won't go into them because they have almost certainly been made obsolete by changes to FreeType.

I have used the same technique for libpng, zlib, expat and libjpeg. (I had some trouble with sqlite, so (for the moment) I have left it in C and avoided namespace conflicts by a massive global renaming, adding a prefix to all symbols.) Yes, I am aware that C and C++ are not entirely compatible, but this has not caused any difficulties, apart from trivial matters like casting the return value of malloc from void* to the desired pointer type in a couple of places, and (in zlib) changing old-style C function declarations to the current C and C++ syntax.

Graham Asher <grahamasher>
Group Member
Sun 02 Aug 2015 11:12:59 AM UTC, original submission:  

Graham Asher suggests the following to put all FreeType functions into a separate namespace.

(i) allow the meaning of FT_BEGIN_HEADER to be either nothing, or 'extern "C"' or 'namespace <something> {' as a configuration option.

(ii) enclose the meat (after #includes) of all source files in FT_BEGIN_SOURCE ... FT_END_SOURCE, and allow FT_BEGIN_SOURCE to be either nothing or 'namespace <something> {'; etc.

Werner LEMBERG <wl>
Group administrator

 

(Note: upload size limit is set to 16384 kB, after insertion of the required escape characters.)

No files currently attached

 

Depends on the following items: None found

Items that depend on this one: None found

 

Carbon-Copy List
  • -email is unavailable- added by mihai (Posted a comment)
  • -email is unavailable- added by mihai (Voted in favor of this item)
  • -email is unavailable- added by grahamasher (Posted a comment)
  • -email is unavailable- added by wl (Submitted the item)
  •  

    There is 1 vote so far. Votes easily highlight which items people would like to see resolved in priority, independently of the priority of the item set by tracker managers.

     

    Follows 1 latest change.

    Date Changed by Updated Field Previous Value => Replaced by
    2015-10-20 mihai Carbon-Copy- Added mihai

    Back to the top

    Powered by Savane 3.13-cf05.
    Corresponding source code