9

I have a huge library and couple classes planned to be decommissioned.

E.g.:

class [[deprecated]] OldApi {
// details
};

The idea is that everything what uses this class is deprecated. E.g.: a hidden dependency looks like this in header:

// on API we have no old API dependency
void hiddenDependency();

and in compiling unit:

void hiddenDependency()
{
  OldApi inst;
  // work on it/do something
}

I have really deep dependency graph so manual solution would be a pain. Is there any way/tool to inherit these deprecate attributes?

So after using this tool, the hidden dependencies also would be deprecated:

[[deprecated]]
void hiddenDependency();

Many thanks for help!

Naszta
  • 7,560
  • 2
  • 33
  • 49
  • 1
    In practice, it does not make much sense to do too much refactoring at once. So you compile with `OldApi` deprecated and then fixes those. If you really want to also deprecated `hiddedDependency` instead of rewriting it, then add `[[deprecated]]` to it. Fix one problem at a time. It might take a bit longer initially but usually you will have less regression and have less debugging to do. Even better, push changes at every step so if something goes wrong, it is easier to find when a bug was introduce. – Phil1970 Aug 09 '20 at 19:00
  • I am not sure that this approach will work. Are you really sure that every API implemented by use of `class OldApi` can be marked deprecated? Or might there be APIs for which the implementation needs to be changed to the new API? Simple example: an init function that now needs to initialize old and new API, but shall not be marked as deprecated. – Werner Henze Aug 10 '20 at 19:14
  • I still don't understand the motivation for this question. This approach would balloon the amount of warnings/errors out substantially if `hiddenDependency()` is called a lot; which doesn't make any sense since `hiddenDependency()` can simply be updated to _not use `OldApi` which undoes the deprecation. It would be backwards to have an API suddenly become deprecated, and then undo its deprecation. What exactly is the issue with addressing the single `[[deprecated]]` call rather than recursively applying it to all callers? – Human-Compiler Aug 13 '20 at 19:26

5 Answers5

3

I have really deep dependency graph so manual solution would be a pain. Is there any way/tool to inherit these deprecate attributes?

Yes, you can write a tool using a compiler like Clang as a library.

Roughly, you would walk the tree that Clang gives you to find function/method bodies. In each of them, check if there are any references to OldApi or its members. If so, add the attribute to the function/method and output the resulting tree.

If you need to deprecate users of those functions too, you can apply it recursively as needed.

Conceptually easy, but if you have never written a tool like that, it may take a while. Even with experience, you usually need to have a lot of code to modify to this approach worth the time investment.


Having said that, as others have pointed out, the question is odd. When you deprecate something, you typically need to replace that thing in the callers/users, rather than deprecate all the users.

Acorn
  • 24,970
  • 5
  • 40
  • 69
3

Strictly speaking, hiddenDependency is not deprecated, and [[deprecated]] in OldApi warns hiddenDependency that OldApi is deprecated and it should be updated/replaced, but the body of hiddenDependency is still valid, unless the code has not been designed properly (and same declaration but different definition in OldApi affects hiddenDependency). Nonetheless, if there is some kind of relation between OldApi and hiddenDependency, they may or might be scoped in a namespace (or should have been), so the solution is to deprecate the namespace instead of the class and trying to "inherit" [[deprecated]]:

namespace [[deprecated("Use NewFunctionality instead")]] OldFunctionality {
  class OldApi {
    public:
        OldApi(){ 
        }
  };

  void foo() {
    OldApi bar;
  }
}

Assuming that you are able to deprecate the caller, what's the limit? Should only be the caller of the deprecated method considered as deprecated? Should be the caller of the caller considered as deprecated as well? How many levels of deprecation are expected? Because the compiler warns that hiddenDependency is calling a deprecated method, it points out the reason and the affected line in hiddenDependency. What's the criteria to not move up the warning until the main? Going further, is the criteria to deprecate all the functions which call OldApi? (if so, you already have a group, hence you can encapsulate it i.e. namespace) Or is the criteria all the functions which call OldApi and some extra requirements? (if so, there is not a clear procedure and it has to be done manually).

Even if parsing the whole text looking for the pattern "hiddenDependency calls OldApi" is an option (and probably the most valid one considering your example), it doesn't look like a clean and easy to maintain (or even justify/document) solution.

Jose
  • 3,306
  • 1
  • 17
  • 22
2

Assuming this is a good idea, my approach would be to temporarily delete or rename OldApi and attempt to compile. Your IDE or existing language tooling should be able to emit information about every compiler error, whether it be definitions that reference OldApi, or functions/class declarations that leverage it. Mark every single one as [[deprecated]]. Then, restore OldApi with the [[deprecated]] flag as well.

jeremyong
  • 445
  • 4
  • 15
2

If the constructor of OldApi is not sacrosanct, then perhaps, a trivial logging mechanism would do;

#include <cstdio>
#include <mutex>
#include <iostream>

#define LOG(msg) error_log( __FILE__, __FUNCTION__, __LINE__, msg )
int error_log (const char* file, const char* function, int line, const std::string& msg) {
   std::cerr << "[" << file << "] " << function << "; " << line << "; " << msg << std::endl;
   return 0;
}
class [[deprecated]] OldApi {
    public:
    static constexpr char status[55] = "deprecated";
    OldApi() {
        //#warning "this is deprecated! ";
        LOG(status);
    }
};
class inherited: public OldApi {};
class in_inherited: public inherited {};
void some_fn() { in_inherited ih; }

int main() {
    some_fn();
    puts("this is working");

    return 0;
}

Working prototype on explorer

0

I dont know if you can inherit the attribute or not, but you can generate the call graph for all the callers (direct and indirect) for this deprecated function. And then put the Deprecated attribute on them (manual or by scripting).

Here's the post which talks about tools to generate call graph : How to Generate a calling graph for C++ code

Harish
  • 41
  • 2