Как найти круговые зависимости?
Может ли кто-нибудь предложить мне инструмент для поиска круговых зависимостей? Я попытался с графиком проекта, но у него сотни файлов заголовков, поэтому их очень сложно найти.
Я редактирую сообщение со значением круговой зависимости:
- Файл A.h имеет защитный элемент #include "B.h".
- Файл B.h имеет защитный элемент #include "A.h".
Спасибо.
Ответы
Ответ 1
Я нашел один способ получить круговые зависимости:
-
Создайте файл DOT, который описывает ориентированный граф зависимости #include, используя cinclude2dot.pl Perl script.
./cinclude2dot.pl --src path_to_include_dir graph.dot
-
Разложить направленный граф на сильно связанные компоненты (круговые зависимости):
sccmap -v graph.dot
Ответ 2
Вы можете запросить возможные или фактические циклы включения, поскольку директивы предварительной обработки на самом деле являются языком, который нужно отлаживать...
Чтобы узнать о фактических циклах, вы можете использовать препроцессор 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, должен быть полезен для выявления проблемы.
Чтобы узнать о возможных циклах, приблизительный анализ, который рекурсивно посещает исходные файлы, должен быть легко осуществим, используя карту для отслеживания включенных файлов.
edit: здесь набросана программа для такого приблизительного анализа
#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;
}
}
Ответ 3
В соответствии с вашей проблемой вы работаете с количеством файлов заголовков. Одним из решений этого является,
если вы удалите определения метода из файлов заголовков и пусть классы содержат только декларации методов и объявления/определения переменных. Определения методов должны быть помещены в файл .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;
}