1

If I use the boost type erasure library in a Windows environment and C++ Builder 11.3 I get compilation errors.

In a C++ Builder 11.3 Windows vcl application I include this header file in a form's .cpp file:

#ifndef BOOST_TEST_H
#define BOOST_TEST_H

#include <boost/type_erasure/any.hpp>
#include <boost/type_erasure/any_cast.hpp>
#include <boost/type_erasure/builtin.hpp>
#include <boost/type_erasure/operators.hpp>
#include <boost/type_erasure/member.hpp>
#include <boost/type_erasure/free.hpp>
#include <boost/mpl/vector.hpp>

namespace boost {
BOOST_TYPE_ERASURE_MEMBER(ReadStatus)
}

#endif

I get this error message:

[bcc32c Error] member11.hpp(46): expected identifier

This takes me to boost header member111.hpp, line 42 (boost 1.82.0)

template<class P, template<class ...> class interface, class Sig, class Concept, class Base, class ID>
using choose_member_interface = typename ::boost::mpl::if_c< ::boost::is_reference<P>::value,
    typename ::boost::mpl::if_c< ::boost::type_erasure::detail::is_non_const_ref<P>::value,
        interface<Sig, Concept, Base, const ID>,
        Base
    >::type,
    interface<Sig, Concept, Base, ID>
>::type;

The problem is that, because C++ Builder VCL for windows has already pulled in the windows headers, including combaseapi.h, which in line 170 had defined 'interface' as something else and this interferes with boost's use of it in the above code...

I can replicate the example in MSVC if my header is used in code that has already incorporated Windows.h...

I can 'fix' it by editing boost's codegear.hpp (where C++ Builder specific stuff resides) to undefine 'interface' or even change the code in member11.hpp to rename interface to interface_ (or any unique arbitrary value):

template<class P, template<class ...> class interface_, class Sig, class Concept, class Base, class ID>
using choose_member_interface = typename ::boost::mpl::if_c< ::boost::is_reference<P>::value,
    typename ::boost::mpl::if_c< ::boost::type_erasure::detail::is_non_const_ref<P>::value,
        interface_<Sig, Concept, Base, const ID>,
        Base
    >::type,
    interface_<Sig, Concept, Base, ID>
>::type;

I'm not happy with either 'fix' because I don't want to edit boost and maybe get side effects later and definitely a maintenance issue going on.

Question:

Is there a better fix?

Andy

AndyB
  • 162
  • 8
  • According to the documentation this needs to be inside a namespace https://www.boost.org/doc/libs/1_82_0/doc/html/BOOST_TYPE_ERASURE_MEMBER.html – Alan Birtles Jun 26 '23 at 08:33
  • "This macro can only be used at namespace scope." – 463035818_is_not_an_ai Jun 26 '23 at 08:34
  • Adding the namespace makes no difference - I get the same error. I've edited the code example to reflect the namespace. – AndyB Jun 26 '23 at 08:56
  • This macro accept more then just one argument: https://www.boost.org/doc/libs/1_54_0/doc/html/BOOST_TYPE_ERASURE_MEMBER.html Even name of macro makes obvious that there should be more then one argument. – Marek R Jun 26 '23 at 09:23
  • The macro works fine with just one argument. See their examples: https://www.boost.org/doc/libs/1_82_0/doc/html/boost_typeerasure/basic.html – AndyB Jun 26 '23 at 09:34

2 Answers2

1

You answer your question:

The problem is that, because C++ Builder VCL for windows has already pulled in the windows headers, including combaseapi.h, which in line 170 had defined 'interface' as something else and this interferes with boost's

The solution is to not include the offending headers before that point, not include them at all, or otherwise cause the offending header to behave well e.g. using preprocessor described here: How to tame the Windows headers (useful defines)?

sehe
  • 374,641
  • 47
  • 450
  • 633
  • That's not the solution: as far as I understand it a C++ Builder VCL project brings those headers in right at the start - it can't initialise without them. Boost's library has many workarounds for MSVC and Codegear/Embarcadero's idiosyncrasies but doesn't seem to cater for this problem. I'm sure Remy Lebeau will correct me if I'm wrong here... – AndyB Jun 26 '23 at 10:39
  • Compilers don't write your code, so no. If it "looks like" it's doing that, it's probably indirectly via some precompiled-header setting, which you can disable. This is the only solution, unless you want to alter the boost library to accomodate every other third-party header that contains un-hygienic stuff. – sehe Jun 26 '23 at 10:44
  • C++ Builder is a framework + a C++ compiler. When the framework is used a lot of code is generated. – AndyB Jun 26 '23 at 12:17
  • Yes. None of that contradicts what I said. When writing C or C++ code you always have to work with the compilation model. If some headers are not doing their role to be good citizens (libx, windows.h looking at you) you have to find ways to deal with it. Most often it means isolating the code into translation units that do not require the conflicting headers, and in them define functions that you can safely call across those translation units. It sucks, but you should complain at windows.h – sehe Jun 26 '23 at 12:23
  • I am doing exactly what you suggest and have been from the start. The code at the top of the question is in a self contained header file. It accesses no other headers. The moment I include this header in a C++ Windows VCL app, even in a translation unit that only contains this header and no others, the problem occurs because C++ Builder has already brought Windows.h into scope before I've written a line of code. If I try the NOGDI define in my project, it won't build at all because it excludes things a Windows VCL project needs. I'm guessing you're not a C++ Builder user? – AndyB Jun 26 '23 at 14:12
  • I'm not sure what "VCL app" refers to, but I'll take your word for it. In that case you'll have to hack it, either by altering the headers that are corrupted by the `interface` keyword's definition (I recommend against it) or, much more logically by `#undef`-ining the offending identifier(s). Of course, that might cause problems if you subsequently used those macros, but I'm getting the vibe you didn't need that and also you understand how to fix it. – sehe Jun 26 '23 at 14:15
  • 1
    Thanks @sehe. There's a codegear.hpp in boost where compiler specific stuff resides. #undef interface in the right place in that file works around the issue. Still feels horribly hacky to me... – AndyB Jun 26 '23 at 14:19
  • Agreed 100%. I hate these header pollutors with some zeal. – sehe Jun 26 '23 at 14:22
0

After discussions with Embarcadero support and in the boost-users mailing list, it was agreed that the best fix was to alter boost's member11.hpp as follows:

template<class P, template<class ...> class Interface, class Sig, class Concept, class Base, class ID>
using choose_member_interface = typename ::boost::mpl::if_c< ::boost::is_reference<P>::value,
    typename ::boost::mpl::if_c< ::boost::type_erasure::detail::is_non_const_ref<P>::value,
        Interface<Sig, Concept, Base, const ID>,
        Base
    >::type,
    Interface<Sig, Concept, Base, ID>
>::type;

renaming interface to Interface to avoid the naming clash.

This is necessary because:

  1. C++ Builder VCL Windows projects bring Windows.h in a way that causes the naming clash and the app developer can't prevent this
  2. If a C++ Builder application is multi-threaded then boost will bring in Windows.h as it uses it to support threads. So simply including the boost headers causes the naming clash...

Boost's fix is simple and self-contained...

This was fixed as issue #20 in the boost type erasure library.

AndyB
  • 162
  • 8