I have a pure virtual class that has a pure virtual method that should be const, but unfortunately this is not the case. This interface is in the library, and the class is inherited by several other classes in separate projects.
I am trying to make this method constwithout interruption of compatibility (at least for some time), but I cannot find a way to create a warning when the non-console method is overloaded.
The following is an example of what I have been able to do so far:
- Stage 0 : before the change. There is only a non-const version
Interface::doSomething(), and it is pure virtual. - Stage 1 : during the transition period. There are both constant and non-constant versions of the method
Interface::doSomething(). Both of them have a default implementation to allow both the old style and the new style implementations (they cannot be pure virtual at this stage, since each inherited class overrides only one of them). The const version supports the non-content version to ensure compatibility with older implementations, the non-content version claims, since it should never be called. - Stage 2 : there is only a non-content version of the method
Interface::doSomething()and it is pure virtual.
1 , , Interface::doSomething(), , , , Stage 2, .
, . , GCC, Clang. , , - (, final), , . ?
#include <iostream>
#include <cassert>
class Interface
{
public:
virtual ~Interface() = default;
#if (STAGE == 0)
void callDoSomething() { doSomething(); }
#else
void callDoSomething() const { doSomething(); }
#endif
protected:
#if (STAGE == 0)
virtual void doSomething() = 0;
#elif (STAGE == 1)
[[deprecated("Overload const version instead")]]
virtual void doSomething()
{
assert(false);
}
#endif
#if (STAGE == 1)
virtual void doSomething() const
{
std::cout << __PRETTY_FUNCTION__ << '\n';
std::cout << " calling non const version\n";
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
const_cast<Interface*>(this)->doSomething();
#pragma GCC diagnostic pop
}
#elif (STAGE == 2)
virtual void doSomething() const = 0;
#endif
};
#if (STAGE == 0 || STAGE == 1)
class Implementation_old : public Interface
{
public:
virtual ~Implementation_old() = default;
protected:
virtual void doSomething() override
{
std::cout << __PRETTY_FUNCTION__ << '\n';
}
};
# endif
#if (STAGE == 1 || STAGE == 2)
class Implementation_new : public Interface
{
public:
virtual ~Implementation_new() = default;
protected:
virtual void doSomething() const override
{
std::cout << __PRETTY_FUNCTION__ << '\n';
}
};
#endif
int main(int argc, char *argv[])
{
Interface* iface = nullptr;
#if (STAGE == 0 || STAGE == 1)
iface = new Implementation_old;
iface->callDoSomething();
delete iface;
#endif
#if (STAGE == 1)
std::cout << "-------------------\n";
#endif
#if (STAGE == 1 || STAGE == 2)
iface = new Implementation_new;
iface->callDoSomething();
delete iface;
#endif
iface = nullptr;
return 0;
}
CMakeLists.txt , 3 STAGE
cmake_minimum_required(VERSION 3.5)
project(test_deprecate_non_const)
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
add_executable(main_stage_0 main.cpp)
target_compile_definitions(main_stage_0 PRIVATE STAGE=0)
add_executable(main_stage_1 main.cpp)
target_compile_definitions(main_stage_1 PRIVATE STAGE=1)
add_executable(main_stage_2 main.cpp)
target_compile_definitions(main_stage_2 PRIVATE STAGE=2)