Replies: 2 comments 1 reply
-
This syntax requires C++11, it might not have been available by the time
Isn't this going to follow the usual deprecation path as it was the case with
As I see it,
Assuming I'm wrong in the previous point and
As an alternative to the class IFoo { /* stuff */ };
namespace future {
class IFoo { /* stuff */ };
} Note the same class name is preserved. Alternatives for said namespace: |
Beta Was this translation helpful? Give feedback.
-
A tiny example that shows all issues found so far: Source code:#include <iostream>
/*inline*/ namespace v1
{
class IFoo
{
public:
virtual ~IFoo() = default;
virtual void dofoo() = 0;
virtual void deprecatedMethod() = 0;
virtual void changed1() = 0;
virtual void changed2() = 0;
};
}
namespace v2
{
class IFoo :
public v1::IFoo
{
public:
~IFoo() override = default;
virtual void newMethod() const = 0;
virtual void changed1() const = 0;
virtual void changed2() const = 0;
virtual void changed1() final
{
std::cout << __PRETTY_FUNCTION__ << '\n';
return const_cast<const IFoo*>(this)->changed1();
}
private:
void deprecatedMethod() final
{
std::cout << __PRETTY_FUNCTION__ << '\n';
return const_cast<const IFoo*>(this)->newMethod();
}
virtual void changed2() final
{
std::cout << __PRETTY_FUNCTION__ << '\n';
return const_cast<const IFoo*>(this)->changed2();
}
};
}
class Bar1 : public v1::IFoo
{
void dofoo() override
{
std::cout << __PRETTY_FUNCTION__ << "\n\n";
}
void deprecatedMethod() override
{
std::cout << __PRETTY_FUNCTION__ << "\n\n";
}
void changed1() override
{
std::cout << __PRETTY_FUNCTION__ << "\n\n";
}
void changed2() override
{
std::cout << __PRETTY_FUNCTION__ << "\n\n";
}
};
class Bar2 : public v2::IFoo
{
void dofoo() override
{
std::cout << __PRETTY_FUNCTION__ << "\n\n";
}
void newMethod() const override
{
std::cout << __PRETTY_FUNCTION__ << "\n\n";
}
void changed1() const override
{
std::cout << __PRETTY_FUNCTION__ << "\n\n";
}
void changed2() const override
{
std::cout << __PRETTY_FUNCTION__ << "\n\n";
}
// void deprecatedMethod() override {} // Error (OK: method is final)
// void changed1() override {} // Error (OK: method is final)
// void changed2() override {} // Error (OK: method is final)
};
int main()
{
Bar1 bar1;
Bar2 bar2;
{
std::cout << "--- BEGIN Test 1\n";
auto* x = dynamic_cast<v1::IFoo*>(&bar1);
if (!x) {
std::cout << "dynamic cast failed\n";
} else {
x->dofoo();
x->deprecatedMethod();
x->changed1();
x->changed2();
}
std::cout << "--- END Test 1\n";
}
{
std::cout << "--- BEGIN Test 2\n";
const auto* x = dynamic_cast<v1::IFoo*>(&bar1);
if (!x) {
std::cout << "dynamic cast failed\n";
} else {
// x->dofoo(); // Cannot call (OK: non-const method)
// x->deprecatedMethod(); // Cannot call (OK: non-const method)
// x->changed1(); // Cannot call (OK: non-const method)
// x->changed2(); // Cannot call (OK: non-const method)
}
std::cout << "--- END Test 2\n";
}
{
std::cout << "--- BEGIN Test 3\n";
auto* x = dynamic_cast<v2::IFoo*>(&bar2);
if (!x) {
std::cout << "dynamic cast failed\n";
} else {
x->dofoo();
x->newMethod();
// x->deprecatedMethod(); // Cannot call (OK: method is not available in the new interface)
x->changed1(); // Runtime error (BAD!!! recursive call!)
// x->changed2(); // Cannot call (BAD!!! cannot call on a non-const object)
}
std::cout << "--- END Test 3\n";
}
{
std::cout << "--- BEGIN Test 4\n";
const auto* x = dynamic_cast<const v2::IFoo*>(&bar2);
if (!x) {
std::cout << "dynamic cast failed\n";
} else {
// x->dofoo(); // Cannot call (OK: non-const method)
x->newMethod();
// x->deprecatedMethod(); // Cannot call (method is not available in the new interface)
x->changed1();
x->changed2();
}
std::cout << "--- END Test 4\n";
}
{
std::cout << "--- BEGIN Test 5\n";
auto* x = dynamic_cast<v1::IFoo*>(&bar2);
if (!x) {
std::cout << "dynamic cast failed\n";
} else {
x->dofoo();
x->deprecatedMethod();
x->changed1();
x->changed2();
}
std::cout << "--- END Test 5\n";
}
{
std::cout << "--- BEGIN Test 6\n";
const auto* x = dynamic_cast<v1::IFoo*>(&bar2);
if (!x) {
std::cout << "dynamic cast failed\n";
} else {
// x->dofoo(); // Cannot call (OK: non-const method)
// x->deprecatedMethod(); // Cannot call (OK: non-const method)
// x->changed1(); // Cannot call (OK: non-const method)
// x->changed2(); // Cannot call (OK: non-const method)
std::cout << "--- END Test 6\n";
}
}
} Compiler output (compiled with `-Wall -Wextra -Woverloaded-virtual`):
Execution output:
Unfortunately the |
Beta Was this translation helpful? Give feedback.
-
Working on #2587, I came up with this idea to deprecate the old interfaces and replace them gradually with new ones, and I'm trying to figure out if this will work:
Assuming we have an interface
IFoo
If we add an
IFoo2
interface that derives fromIFoo
where old pure virtual methods are declaredprivate
andfinal
and implemented inline using the new methods, for example:IFoo
interface will see no differences (perhaps they will get a deprecated warning for implementing a deprecated interface)IFoo
interface will be able to use any device implementingIFoo
orIFoo2
(and perhaps will a deprecated warning when using the deprecated methods)IFoo2
will automatically implement alsoIFoo
. They will not be able to implement the deprecated methods, since they are declared finalIFoo2
will be able to use only the devices implementing the new interface, it will not be able to use the deprecated methods, since they are private for theIFoo2
interface, but they can eventually try to use theIFoo2
, and switch to theIFoo
interface, if the new one is not available.The only problem that I can see right now, is that we are back to the "2" interfaces... But in this way, we don't have to add methods that are not pure virtual to the interfaces, and we do not break the API/ABI for changing method signatures or adding new ones
Is there any drawback I'm not seeing right now? Is there a reason why we are not already doing this in this way right now?
Edit: One extra drawback is when we want to disable the deprecated parts in YARP (i.e.
YARP_NO_DEPRECATED
), theIFoo
interface will disapperar, and theIFoo2
interface will stay, so all the methods that are not explicitly declared inIFoo2
will not exist, therefore they should be declared explicitly in the#else
branch of the#ifndef YARP_NO_DEPRECATED
. Also when we allow breaking changes for major releases, we cannot just remove deprecated part, we also have to take care of the2
in the names.Edit 2: Another drawback is that, if you change the signature of the method, and keep the same name, when you implement a class that inherits from the new interface, we will get a
-Woverloaded-virtual
warning, because the old method is hidden. This is an annoying issue, because it means that the user will have to addusing
to hide this warning for methods that areprivate
andfinal
.Edit 3: Yet another drawback. If a method is changed from non-const to const, and the non-const version is private, the method can be called only from a const object (fix: in this case it should have the same visibility as the non-const version)
Beta Was this translation helpful? Give feedback.
All reactions