问题 我怎样才能找到循环依赖?


有人可以建议我找到循环依赖的工具吗?我尝试了一个项目图,但它有数百个头文件,所以找到它们非常复杂。

我编辑了具有循环依赖意义的帖子:

  • 文件A.h有一个#include“B.h”后卫。
  • 文件B.h有#include“A.h”后卫。

谢谢。


2052
2018-03-27 08:15


起源

什么之间的循环依赖?我可以想到各种意义。你能详细说明吗? - FireAphis


答案:


我找到了一种获得循环依赖的方法:

  1. 生成一个DOT文件,该文件使用描述#include依赖关系有向图 cinclude2dot.pl Perl脚本。

    ./cinclude2dot.pl --src path_to_include_dir graph.dot

  2. 将有向图分解为强连通分量(循环依赖):

    sccmap -v graph.dot


14
2018-03-28 05:53





你可以查询 可能 要么 实际 包含周期,因为实际上是预处理指令  一种语言,待调试......

要了解 实际 循环,你可以使用预处理器 CPP 有选项

-M  Instead of outputting the result of preprocessing, output a rule suitable for make describing the dependencies of the main source file...

或更好

-MM Like -M but do not mention header files that are found in system header directories, nor header files that are included, directly or indirectly, from such a header.

-MF file
       When used with -M or -MM, specifies a file to write the dependencies to.  If no -MF switch is given the preprocessor sends the rules to the same place it would have sent preprocessed output.

在找到循环时,您将收到嵌套深溢出的错误,并且使用-MF指定的输出对于发现问题非常有用。

要了解 可能 循环近似分析,递归访问源文件,应该很容易实现,使用地图来跟踪包含的文件。

编辑:这里是草拟一个程序进行这种近似分析

#include <set>
#include <vector>
#include <string>
#include <fstream>
#include <cstdlib>
#include <iostream>
#include <iterator>
#include <algorithm>
#include <stdexcept>

#include <boost/foreach.hpp>
#include <boost/filesystem.hpp>
#include <boost/program_options.hpp>

using namespace std;
using namespace boost;
using namespace boost::filesystem;
using namespace boost::program_options;

struct inclusions
{
    inclusions(int argc, char **argv)
    {
        options_description ops("detect_loops usage");
        ops.add_options()
             ("include,I", value< vector<string> >(), "search paths")
             ("file,F",    value< string >(),         "file to be analyzed");
        variables_map vm;
        store(parse_command_line(argc, argv, ops), vm);
        notify(vm);

        path start = locate(vm["file"].as<string>());
        simstack.push_back(start);

        // file directory is always search start
        include_paths.push_back(start.parent_path());

        if (vm.count("include"))
        {
            vector<string> s = vm["include"].as< vector<string> >();
            copy(s.begin(), s.end(), back_inserter(include_paths));
        }

        scan_includes();
    }

    typedef vector<path> t_paths;
    t_paths include_paths;

    t_paths simstack;

    typedef vector<t_paths> t_cycles;
    t_cycles cycles;

    set<path> analyzed;

    path locate(string file)
    {
        path p(file);
        if (exists(p))
            return p;
        BOOST_FOREACH(path i, include_paths)
        {
            path q = i / p;
            if (exists(q))
                return q;
        }
        throw domain_error(file + " not fund");
    }

    void scan_includes()
    {
        path c = simstack.back();
        if (analyzed.find(c) != analyzed.end())
            return;

        ifstream f(c.string());
        string l;
        while (getline(f, l))
        {
            char included[256 + 1];
            if (sscanf(l.c_str(), " # include \"%256[^\"]\"", included) == 1)
            {
                path p = locate(included);

                // check loops before recurse
                t_paths::iterator g = find(simstack.begin(), simstack.end(), p);
                if (g != simstack.end())
                {
                    t_paths loop(g, simstack.end());
                    loop.push_back(p);
                    cycles.push_back(loop);
                }
                else
                {
                    simstack.push_back(p);
                    scan_includes();
                    simstack.pop_back();
                }
            }
        }

        analyzed.insert(c);
    }
};

int main_detect_loops(int argc, char **argv)
{
    try
    {
        inclusions i(argc, argv);
        BOOST_FOREACH(inclusions::t_paths p, i.cycles)
        {
            copy(p.begin(), p.end(), ostream_iterator<path>(cout, ","));
            cout << endl;
        }
        return 0;
    }
    catch(const std::exception &e)
    {
        cerr << e.what() << endl;
        return 1;
    }
}

2
2018-03-27 10:42



'-MM'方法仅在头文件没有包含保护时才有效,因此它在实践中永远不会起作用 - anatolyg


根据您的问题,您可以使用多个头文件。一个解决方案是, 如果从头文件中删除方法定义,并让类只包含方法声明和变量声明/定义。方法定义应放在.cpp文件中(就像最佳实践指南所说)。

//A.h 
#ifndef A_H 
#define A_H 
class B; 
class A 
{ 
        int _val; 
        B* _b; 
public: 

        A(int val); 
        void SetB(B *b); 
        void Print(); 
}; 
#endif 

//B.h 
#ifndef B_H 
#define B_H 
class A; 
class B 
{ 
        double _val; 
        A* _a; 
public: 

        B(double val); 
        void SetA(A *a); 
        void Print(); 
}; 
#endif 

//A.cpp 
#include "A.h" 
#include "B.h" 

#include <iostream> 

using namespace std; 

A::A(int val) 
:_val(val) 
{ 
} 

void A::SetB(B *b) 
{ 
        _b = b; 
        cout<<"Inside SetB()"<<endl; 
        _b->Print(); 
} 

void A::Print() 
{ 
        cout<<"Type:A val="<<_val<<endl; 
} 

//B.cpp 
#include "B.h" 
#include "A.h" 
#include <iostream> 

using namespace std; 

B::B(double val) 
:_val(val) 
{ 
} 

void B::SetA(A *a) 
{ 
        _a = a; 
        cout<<"Inside SetA()"<<endl; 
        _a->Print(); 
} 

void B::Print() 
{ 
        cout<<"Type:B val="<<_val<<endl; 
} 

//main.cpp 
#include "A.h" 
#include "B.h" 

int main(int argc, char* argv[]) 
{ 
        A a(10); 
        B b(3.14); 
        a.Print(); 
        a.SetB(&b); 
        b.Print(); 
        b.SetA(&a); 
        return 0; 
} 

0
2018-03-27 09:26



谢谢你的回答,但我无法修改代码。我只需要知道哪些文件具有循环依赖性。 - Emilio
A.h包括B.h和B.h包括A.h。在c ++中,这并不是真正允许的,因为它们往往会一遍又一遍地相互包含,从而导致无限循环。 - IndieProgrammer
这需要一个永恒 - Yousuf Azad