Application Configuration Files for dlls

To create an Application Configuration File (or here for Windows XP) for a DLL it is necessary to include the manifest resource ID in the name of the .config file.

E.g. to redirect attempts to load the 8.0.50608.0 version of VC++ CRT, referenced in the cpp.native.dll assembly of the following example:

app.exe
cpp.managed.dll
cpp.native.dll
Microsoft.VC80.CRT.manifest
msvcm80.dll
msvcp80.dll
msvcr80.dll

One needs to create a “cpp.native.dll.2.config” file (the number denotes a resource ID of the manifest, embedded into the module, which is almost always “2” for dlls):

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<configuration>
  <windows>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity
          name="Microsoft.VC80.CRT"
          publicKeyToken="1fc8b3b9a1e18e3b"
          type="win32"
          processorArchitecture="x86" />
        <bindingRedirect
          oldVersion="8.0.41204.256-8.0.50727.762"
          newVersion="8.0.50727.762" />
      </dependentAssembly>
    </assemblyBinding>
  </windows>
</configuration>

and put it in the same directory:

app.exe
cpp.managed.dll
cpp.native.dll
cpp.native.dll.2.config
Microsoft.VC80.CRT.manifest
msvcm80.dll
msvcp80.dll
msvcr80.dll

Creating app.exe.config doesn’t help, because the “wrong” dependencies are in the cpp.native.dll, and not in the app.exe itself.

Resources

To better understand mechanics behind locating side-by-side assemblies, one can refer to the .NET Fusion Workshop. 13. Unmanaged Assemblies by Richard Grimes and to monitor loading the application in question with the Process Monitor or FileMon.

And another useful tool in diagnosing side-by-side assemblies dependencies problems is a fresh version of the Dependency Walker.

This application has failed to start because the application configuration is incorrect.

So we’ve created our managed C++ application, using Visual Studio 2005, and it was fun and surprisingly easy thanks to the C++/CLI, so we happily gave it to the QA or a customer, and— well, it didn’t take off, the operating system just refused to launch it. And the error message wasn’t very descriptive either:

Unhandled Exception: System.IO.FileLoadException: Could not load file or assembly 'cpp.managed, Version=..., Culture=..., PublicKeyToken=...' or one of its dependencies. <…>
---> System.Runtime.InteropServices.COMException (0x800736B1): This application has failed to start because the application configuration is incorrect. Reinstalling the application may fix this problem. (Exception from HRESULT: 0x800736B1)
at app.Main(String[] argv)

It’s a little bit less of information, than is needed to get clear understanding of what’s going on.

Configuration

Let’s imagine that we have the following components:

app.exe
cpp.managed.dll
cpp.native.dll

I.e. a managed application app.exe, which depends on a C++/CLI cpp.managed.dll, which in turn depends on a native cpp.native.dll.

Finding out what’s wrong

First of all, let’s open the Event Viewer, and try to find error messages with the Source = SideBySide.

Windows XP or Windows Server 2003

We are likely to get several entries in the “System” Event Log, neither of which has full version information of the missing dependency:

Generate Activation Context failed for cpp.managed.dll. Reference error message: The referenced assembly is not installed on your system.

Resolve Partial Assembly failed for Microsoft.VC80.CRT. Reference error message: The referenced assembly is not installed on your system.

Dependent Assembly Microsoft.VC80.CRT could not be found and Last Error was The referenced assembly is not installed on your system.

Vista

There we should find in the Event Log something like follows:

Activation context generation failed for "cpp.managed.dll". Dependent Assembly Microsoft.VC80.CRT, processorArchitecture="x86", publicKeyToken="1fc8b3b9a1e18e3b", type="win32", version="8.0.50727.762" could not be found. Please use sxstrace.exe for detailed diagnosis.

Fixing it

All we need is to deploy VC++ runtime to the target system.

For the purpose of our example let’s imagine that we didn’t want to redistribute VC++ CRT as shared assemblies (installing it into C:\Windows\WinSxS), but instead decided to redistribute them as private assemblies. Here’s how it can look like:

app.exe
cpp.managed.dll
cpp.native.dll
Microsoft.VC80.CRT.manifest
msvcm80.dll
msvcp80.dll
msvcr80.dll

How it can fail again, or split dependencies

Of course the life is not always easy. We did the above, and it still fails with the same error message. Well, now we know what to do, and we rush to the Event Viewer.

Windows XP or Windows Server 2003

In this case Event Log has something along the following lines:

Generate Activation Context failed for cpp.native.dll. Reference error message: A component version required by the application conflicts with another component version already active.

Unfortunately, the message does not mention neither the dependencies, nor their versions which conflict. To find that out we have several options of inspecting the cpp.native.dll manifest: * taking a look at the *.intermediate.manifest or *.embed.manifest files in the intermediate build directory, * using a resource editor or some other tool to view the manifest, actually embedded into the cpp.native.dll, * or we could just use a text viewer, and search for the “dependentAssembly” string in the binary.

E.g. here’s the output of a combination of strings and grep utilities (reformatted for readability):

> strings cpp.native.dll | grep dependentAssembly

<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
  <assemblyIdentity
    name="cpp.native"
    processorArchitecture="x86"
    type="win32"
    version="1.0.0.0" />
  <dependency>
    <dependentAssembly>
      <assemblyIdentity
        type="win32"
        name="Microsoft.VC80.CRT"
        version="8.0.50727.762"
        processorArchitecture="x86"
        publicKeyToken="1fc8b3b9a1e18e3b" />
    </dependentAssembly>
  </dependency>
  <dependency>
    <dependentAssembly>
      <assemblyIdentity
        type="win32"
        name="Microsoft.VC80.CRT"
        version="8.0.50608.0"
        processorArchitecture="x86"
        publicKeyToken="1fc8b3b9a1e18e3b" />
    </dependentAssembly>
  </dependency>
</assembly>

Isn’t it strange, that we have dependency on two different versions of Microsoft.VC80.CRT? Well, with Visual Studio 2005 SP1 out it’s quite possible, and in our case cpp.native.dll indeed was linked with some static libraries, compiled with pre-SP version of Visual Studio 2005.

Vista

Due to the Vista improved side-by-side errors diagnostics, there Event Log should contain just enough information to understand what’s going on:

Activation context generation failed for "cpp.native.dll". Error in manifest or policy file “” on line . A component version required by the application conflicts with another component version already active. Conflicting components are:.
Component 1: C:\Windows\WinSxS\manifests\ x86_microsoft.vc80.crt_1fc8b3b9a1e18e3b_8.0.50727.312_none_10b2ee7b9bffc2c7.manifest.
Component 2: Microsoft.VC80.CRT.MANIFEST.

Fixing it (again)

An obvious (and correct) solution is to rebuild all the libraries with the new version of Visual Studio 2005. If we cannot do it, or just want to save some time during development, and postpone rebuilding the dependencies for later time, we’d want to try workarounds, mentioned by Nikola Dudar in his blog:

  • Workaround#1:

    Install the newer version (8.0.50727.762 in this case) of VC++ MSMs or VCRedist.EXE on a machine where your application is going to run. Once policy for VC++ assemblies is installed on that machine they are going to redirect all loads of older versions (8.0.50608.0) to the newest version available on the machine.

Note that to apply this workaround we’d need to download the updated vcredist.exe.

  • Workaround#2:

    If you are redistributing VC++ libraries in application’s local folder, you need to add an application configuration file that redirects an attempt to load 8.0.50608.0 version to 8.0.50727.762 version. Configuration file has to have same name as the exe plus .config extension and has to be right next to exe or embedded into the EXE. Here is an example of a configuration file that one would use to resolve issue with the manifest from above:

For this to work in our case we’d need to apply some magic.

ImageMagick configure JPEG PNG TIFF etc failed tests

If “cd /usr/ports/graphics/ImageMagick/ && make install” or “make configure” complains about JPEG, PNG, TIFF etc failed tests, make sure you didn’t forget to include WITHOUT_X11=yes option while making it on a system without X11.

SCM: New links section

Sorry, no new “real” blogging yet. Just wanted to announce new SCM section in links.

Jonathan Caves Blog. Inside Visual C++

In Jonathan’s own words:

I’ve been working on compilers for longer than I care to remember (Pascal, Fortran, Modula-2, C and most recently C++). For the last 10 years I’ve been a developer on the Visual C++ compiler team: currently I am also the Microsoft representative on the ISO C++ committee WG21/X3J16.

The main focus of my current activities is C++ for the .NET platform.

I am hoping that I can use this blog to provide insights into the decision making and development process behind Visual C++: basically what we’re doing and why we’re doing it.

C++ analogs for 'Generics: really weird'

Just for the record, here’s what conforming C++ implementation should print for programs, similar to the ones from “Generics: really weird”. Probably I can save you some time saying that the output is the same for all the three versions.

bool
int
int

We’ll assume that all of the programs are preceeded with the following definitions:

#include <stdio.h>

void print_type_name( int& )  { printf("int\n"); }
void print_type_name( char& ) { printf("char\n"); }
void print_type_name( bool& ) { printf("bool\n"); }

Original version

template< class T1 >
struct A
{
    T1 a;

    template< class T2 >
    struct B : A<T2>
    {
        T1 b;

        template< class T3 >
        struct C : B<T3>
        {
            T1 c;
        };
    };
};

int main()
{
    A<int>::B<char>::C<bool> o;

    print_type_name(o.a);
    print_type_name(o.b);
    print_type_name(o.c);
}

The output:

bool
int
int

“The same” member and base class

template< class T1 >
struct A
{
    T1 a;

    template< class T2 >
    struct B : A<T2>
    {
        A<T2> a_;
        T1 b;

        template< class T3 >
        struct C : B<T3>
        {
            B<T3> b_;
            T1 c;
        };
    };
};

int main()
{
    A<int>::B<char>::C<bool> o;

    print_type_name(o.b_.a_.a);
    print_type_name(o.b_.b);
    print_type_name(o.c);
}

The output:

bool
int
int

Nothing to write home about, it is the same as before.

And now without inheritance

template< class T1 >
struct A
{
    T1 a;

    template< class T2 >
    struct B
    {
        A<T2> a_;
        T1 b;

        template< class T3 >
        struct C
        {
            B<T3> b_;
            T1 c;
        };
    };
};

int main()
{
    A<int>::B<char>::C<bool> o;

    print_type_name(o.b_.a_.a);
    print_type_name(o.b_.b);
    print_type_name(o.c);
}

The output:

bool
int
int

Still the same.


VC++ 7.1 has a bug preventing it from producing correct results. The bug seems to be fixed in current builds of VC++ 8.0. CodeWarrior, GCC, and Comeau produce the same (correct) results as VC++ 8.0.

Generics: really weird

Original version

Cyrus published an interesting example with C# generics:

using System;

class Program
{
    public class A<T1>
    {
        public T1 a;

        public class B<T2> : A<T2>
        {
            public T1 b;

            public class C<T3> : B<T3>
            {
                public T1 c;
            }
        }
    }

    static void Main()
    {
        A<int>.B<char>.C<bool> o = new A<int>.B<char>.C<bool>();

        System.Console.WriteLine(o.a.GetType().FullName);
        System.Console.WriteLine(o.b.GetType().FullName);
        System.Console.WriteLine(o.c.GetType().FullName);
    }
}

It (somewhat surprisingly) prints:

System.Boolean
System.Char
System.Int32

Explanation

If you want to understand why the result is what it is, please refer to the comments by Stuart Ballard and CyrusN.

In short here it is. C# 2.0 specification says (10.8):

  • <…> if the namespace-or-type-name appears within the body of a type declaration, then for each instance type T (§25.1.2), starting with the instance type of that type declaration and continuing with the instance type of each enclosing class or struct declaration (if any):

    • If K is zero and the declaration of T includes a type parameter with name I, then the namespace-or-type-name refers to that type parameter.

    • Otherwise, if T contains a nested accessible type having name I and K type parameters, then the namespace-or-type-name refers to that type constructed with the given type arguments. If there is more than one such type, the type declared within the more derived type is selected.

A<T1>.B<T2>.C<T3> is derived from a B<T3>. B<T3> is an unqualified name, so the compiler tries to find which particular [???.]B<T3> it refers to according to the rules above. Since B<T3> is used within the definition of A<T1>.B<T2>, B<T3> can refer to A<T1>.B<T3>. On the other hand B<T2> is derived from A<T2>, and it has its own B<T3>, which is A<T2>.B<T3>. Specification says that the definition from the most derived type is preferred, and that is A<T2>.B<T3>. I.e. we can change the definition of C<T3> above to the following equivalent one:

public class C<T3> : A<T2>.B<T3>
{
    public T1 c;
}

Let’s instantiate the class A<int>.B<char>.C<bool>. It’s defined within the scope of A<int>.B<char>, and, as we concluded above, derived from the A<char>.B<bool>, which in turn is derived from A<bool>:

public class A<int>
{
    int a;

    public class B<char> : A<char>
    {
        int b;

        public class C<bool> : A<char>.B<bool>
        {
            int c;
        }
    }
}

public class A<char>
{
    char a;

    public class B<bool> : A<bool>
    {
        char b;
    }
}

public class A<bool>
{
    bool a;
}

It seems that we’re ready to answer the question about the output of the program.

  1. o.a is a member of A<bool> in our example. T1 within the definition of A<bool> is resolved to bool since K is zero for T1 and A<bool> has a type parameter with the name T1, bound to bool. Therefore the type of o.a is bool — this is the first line printed by the program in question.

  2. o.b is a member of A<char>.B<bool>. A<char>.B<bool> does not have type parameters named T1, so enclosing class, A<char> is examined. It does have a type parameter T1, bound to char. Therefore the type of o.b is char — this is the second line printed by the program.

  3. Finally, o.c is a member of A<int>.B<char>.C<bool>. A<int>.B<char>.C<bool> does not have type parameters with name T1, so compiler goes to enclosing scope, A<int>.B<char>. That one does not have T1 type parameters either, so its enclosing class, A<int> is examined where T1 bound to int is found. So the type of o.c is int — this is the last line printed by the program.

“The same” member and base class

Now the interesting question is what would B<T3> mean being used within the definition of C<T3>? For instance let’s try to add some members having the “same” type as base classes:

using System;

class Program
{
    public class A<T1>
    {
        public T1 a;

        public class B<T2> : A<T2>
        {
            public A<T2> a_ = new A<T2>();
            public T1 b;

            public class C<T3> : B<T3>
            {
                public B<T3> b_ = new B<T3>(); // looks the same as the base class, doesn't it?
                public T1 c;
            }
        }
    }

    static void Main()
    {
        A<int>.B<char>.C<bool> o = new A<int>.B<char>.C<bool>();

        System.Console.WriteLine(o.b_.a_.a.GetType().FullName);
        System.Console.WriteLine(o.b_.b.GetType().FullName);
        System.Console.WriteLine(o.c.GetType().FullName);
    }
}

Well it turns out that not all the members have the same type as corresponding bases classes. Now A<T3> inherited by B<T3>, which is essentially A<bool>, is playing tricks on us:

System.Boolean
System.Boolean // nope, not the same at all
System.Int32

And now without inheritance

OK. Let’s try another twist. Let’s remove inheritance:

using System;

class Program
{
    public class A<T1>
    {
        public T1 a;

        public class B<T2>
        {
            public A<T2> a_ = new A<T2>();
            public T1 b;

            public class C<T3>
            {
                public B<T3> b_ = new B<T3>();
                public T1 c;
            }
        }
    }

    static void Main()
    {
        A<int>.B<char>.C<bool> o = new A<int>.B<char>.C<bool>();

        System.Console.WriteLine(o.b_.a_.a.GetType().FullName);
        System.Console.WriteLine(o.b_.b.GetType().FullName);
        System.Console.WriteLine(o.c.GetType().FullName);
    }
}

Here’s what it prints:

System.Boolean
System.Int32    // yet another output
System.Int32

Now that’s interesting because this is exactly what C++ program would print! Anyway, I find it more than confusing that the output is different for all three programs above.


Speaking of C++ programs: here they are.

Klaus Kreft, Angelika Langer. After Java and C# - what is next?

How could I have missed this article before?

Java and C# are almost identical programming languages. Boring repetition that lacks innovation. Where is the revolutionary programming language that will stop the redundancy?

<…>

In essence, Java and C# feel like PL/I and consorts: PL/I was an attempt to combine the best features of Fortran, COBOL and ALGOL and was developed by George Radin of IBM in 1964. Java was an attempt to combine the best features of C++ and Smalltalk and was developed by James Gosling of Sun Microsystems in 1995. C# is an attempt to combine the best features of C++, Visual Basic and Java and was developed by Anders Hejlsberg of Microsoft in 2000. Will the next language be an attempt to combine … ?

It reminds me of Alexander Stepanov’s interview:

I spent several months programming in Java. Contrary to its authors prediction, it did not grow on me. I did not find any new insights - for the first time in my life programming in a new language did not bring me new insights.

I can’t stop asking myself the same question: is it still possible for a new language to make a breakthrough like C and C+ did at the time?

Will there be a revolutionary language like C again - invented by programmers rather than corporations? A language that will be fun and exciting to use - free of baggage, convenient, consistent, comprehesible? What would it look like? Do we need such an innovation, or are we happy anyway and cannot even think of a language better than Java or C#?

P.S. And don’t forget about the comments — they’re worth reading.

Return values destructors accessibility

Recently I was really surprised with a disagreement between several major C++ compilers about where destructor accessibility is to be checked for function return values. Attempts to find definitive answer in the C++ International Standard gave no positive outcome. I shared my discovery with Alexy and he, being constructive as usually, gave me an idea. It is brilliant yet simple.

Implementing a C++ compiler is non-trivial to say the least, verifying its correctness is not much easier. At the same time we occasionally encounter cases for which compilers fail to follow the Standard close enough (or the Standard itself is vague). Sometimes we just whine about it, sometimes we report the bug to the compiler writers. The latter is preferred to the former, of course, but how does it help the other compilers vendors?

What if there would be a publicly available collection of such incidents?

I liked the idea so much that I can’t stop thinking about it now. I don’t go as far as thinking about building The Collection of test cases for a C++ compiler, but on the other hand I don’t want to loose the cases I am aware of. I don’t know what to do with them, but I’ll try to record the ones I encounter here.


(1)

struct A {
    struct N {
    private:
        ~N();
    };
    N n_;
};

A f();

int main() {
    f();
}

(2)

struct A {
private:
    ~A();
};

A f();

int main() {
    f();
}

(3)

struct A {
private:
    A(A const&);
    ~A();
};

A f();

int main() {
    f();
}

My intuitive interpretation of the samples above is that neither of them should compile because at the point of the call to f() destructor of its return value is either can’t be implicitly defined, or inaccessible.

But when I tried to compile these examples with several different major compilers, all of them gave different results:

(1) (2) (3)
CodeWarrior 9.5 for Mac OS X failed succeeded succeeded
MinGW GCC 3.4.2 failed failed failed
VC++7.1 succeeded failed failed
Comeau 4.3.3 for Windows succeeded succeeded failed

I was not able to find exact standardeeze which would specify that destructor accessibility is verified for return values of function calls at the point of function call, so I won’t blame compiler vendors for not following my intuition.

Diagnosing "misoverriding" functions in C++

The problem

When a virtual function signature is modified in C++, you have to review all the derived classes and adjust signatures of all the functions which override the one you have just changed. Otherwise compiler will treat those as new (possibly virtual) functions, unrelated to the function(s) in base classe(s).

Keeping virtual functions pure can help sometimes, but does not solve the problem in general:

class A
{
public:
    virtual void f(int)    = 0;
    virtual void g() const = 0
};

class B : public A
{
public:
    virtual void f(int)    { }
    virtual void g() const { }
};

class C : public B
{
public:
    virtual void f(int)    { }
    virtual void g() const { }
};

If we change void A::f(int) to A::f(long) and A::g() const to A::g(), compiler will complain on a first attempt to create an instance of the class A. That we won’t miss. But after class A will be modified appropriately, creating instances of class B will not ring the bell:

class A
{
public:
    virtual void f(long) = 0;
    virtual void g()     = 0;
};

class B : public A
{
public:
    virtual void f(long) { }
    virtual void g()     { }
};

class C : public B
{
public:
    virtual void f(int)    { }
    virtual void g() const { }
};

C c;

Compiler will treat C::f(int) and C::g() const as a new virtual functions, not related to A::f(long) and A::g() or B::f(long) and B::g(), and despite expectations of class C author, B::f(long) and B::g() functions will be called for instances of class C.

C# and D address this problem with override keyword, which will trigger compiler diagnostics when used in a declaration of a function which does not override an existing member of a base class, Eiffel uses redefine for the same purpose.

Some people can argue that such errors can be successfully caught with proper unit tests. I could not agree more, but cost/benefit ratio of failed tests vs. compiler diagnostics is arguably favors the latter.

Some C++ compilers give warnings in similar situations, but most of them remain silent. Even if your compiler is able to notify you about functions in hierarchy having mismatching signatures, chances are that you turned off this warning because there are some cases when you want different functions in base and derived class named the same way. Usually those will be non-virtual, but most of the C++ compilers, which are known to me, are not that smart to emit warnings for mismatching signatures of virtual functions only (with Comeau, based on EDG front end, being an exception). Another issue is that some users tend to ignore warnings, and you may not want to allow them doing that.

So the question is, can we use a C++ compiler to catch errors like this at compile time?

Solution

The answer is yes, we can, and we do it at MetaCommunications. It is even fairly easy thing to do. All we need is to remember the attributes of virtual functions which will break overriders when modified. At least two easily come to mind: return value and exception specification. Not all of the compilers support exception specifications, so we will leave that option aside.

To make compiler errors informative and intentions clear to the users of a class which virtual functions break overriders, let’s introduce a special type with self-describing name:

struct break_override
{
};

Now if we were careful modifying class A and would not just modify signatures of the virtual functions, but would break their overriders and introduce new versions instead, compiler would give us diagnostics we wanted. For example here’s how we could do that via changing return values:

class A
{
public:
    virtual void f(long) = 0;
    virtual void g()     = 0;

    // Deprecated versions.
    virtual break_override f(int)    { return break_override(); }
    virtual break_override g() const { return break_override(); }
};

class B : public A
{
public:
    virtual void f(int)    { }
    virtual void g() const { }
};

Now we’re receiving compilation errors for class B at the point it is defined:


Comeau C/C++ 4.3.3 (Oct 25 2003 12:02:26) for MS_WINDOWS_x86
Copyright 1988-2003 Comeau Computing.  All rights reserved.
MODE:strict warnings C++

"test.cpp", line 19: error: return type is not identical to nor covariant with
      return type "break_override" of overridden virtual function function
      "A::f(int)"
  virtual void f(int)    { }
               ^
"test.cpp", line 20: error: return type is not identical to nor covariant with
      return type "break_override" of overridden virtual function function
      "A::g() const"
  virtual void g() const { }
               ^
2 errors detected in the compilation of "test.cpp".

Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 13.10.3077 for 80x86
Copyright (C) Microsoft Corporation 1984-2002. All rights reserved.

test.cpp
test.cpp(21) : error C2555: 'B::f': overriding virtual function
    return type differs and is not covariant from 'A::f'
    test.cpp(12) : see declaration of 'A::f'
test.cpp(21) : error C2555: 'B::g': overriding virtual function
    return type differs and is not covariant from 'A::g'
    test.cpp(13) : see declaration of 'A::g'

g++.EXE (GCC) 3.4.2 (mingw-special)
Copyright (C) 2004 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

test.cpp:19: error: conflicting return type specified for `virtual void B::f(int)'
test.cpp:12: error:   overriding `virtual break_override A::f(int)'
test.cpp:20: error: conflicting return type specified for `virtual void B::g() const'
test.cpp:13: error:   overriding `virtual break_override A::g() const'

There are two things worth mentioning about this code though: first, it is still possible to call old versions of A::f(int) and A::g() const, and second, it is aesthetically unpleasing to have return break_override() within those functions, since nobody is supposed to call them. We could try to solve the first issue making these functions private, but that would not help for calls within other functions of the same class its friends…

Well, there should have been a simple way to solve both problems. One could think that to do that it is enough to make break_override noncopyable and/or indestructible, and to throw an exception instead of returning result from the deprecated function:

struct break_override
{
private:
    ~break_override();
     break_override(break_override const&);
};

class A
{
public:
    virtual void f(long) = 0;
    virtual void g()     = 0;

    // Deprecated versions.
    virtual break_override f(int)    { throw; }
    virtual break_override g() const { throw; }
};

Alas, it turns out that this approach does not work, at least not for all compilers. Anyway, as it seems, for each particular compiler it is possible to find specific way to make it working, i.e. failing as expected. So a little bit of conditional compilation should do the trick. And obviously for practical usage it’s better to conceal details of the implementation behind a macro:

class A
{
public:
    virtual void f(long) = 0;
    virtual void g()     = 0;

    // Deprecated versions.
    BREAK_OVERRIDE( f(int) );
    BREAK_OVERRIDE( g() const );
};

Which will be left as an exercise to the readers.

Syndicate content