Предыдущий пост -
Следующий пост -

проверка на повторные включения

Рубрика: C++, Дата: 26 February, 2013, Автор:
Tags:

Здорова господа!

Сегодня я от решил попробовать написать первый пост.

Вообщем решим задачку:

Разработайте внешнее средство, которое проверяет перед компиляцией каждый исходный файл на предмет существования повторных включений заголовков и устраняет повторы. Опробуйте это средство на практике и сравните его со способами контроля повторных включений рассмотренных в 9.3.3 . Дает ли такое средство какие либо дополнительные преимущества на этапе выполнения на вашей системе?

Нам нужно создать программку которой будем передавать файл либо количество файлов, если программа будет состоять из нескольких файлов.

Что еще будет делать программка? Скорее всего прочитает файлы, затем разберем мы все include, тоесть запишем их в массив (массив строк), дальше из массива строк выделим заголовочные файлы (файлы с расширением .h), потом просто проверим каждый элемент массива на повторное включение и если будет одинаковые элементы, то мы остановим программу и выведем ошибку, если все нормально выведем Ок!

С предыдущей задачи мы как бы уже делали функцию которая будет выводить все #include. Она принимает название файла и массив указателей и заполняет этот массив. Вот ее код:

//prinimaet nazvanie faila i massiv ctrok, return cpicok включаемых файлов
void readF(string f,char* vf[])
{
	ifstream r(f.data(),ios::in);
	if(!r)
	{
		cerr <<"don't open file"<<endl;
		exit(1);//avar
	}

	char k;
	string result;
	while(r.get(k) !=0)
		result+=k;
	//cout <<result<<endl;
	//ichem includu
	const char* str=new char[result.size()];
	str=result.data();
	//cout <<str<<endl;
	char* leksema=strtok((char*)str,"\n");
	int count=0;
	while(leksema!=NULL)
	{
		//cout <<leksema<<endl;
		//poick include
		int flag=0;
		for(int i=0;leksema[i]!=0;i++)
		{
			if(!isspace(leksema[i])&&leksema[i]!='#')
				break;

			if(leksema[i]=='#')
			{
				if(leksema[i+1]=='i')
					if(leksema[i+2]=='n')
					{
						flag=1;
						break;
					}
			}
		}

		if(flag==1)
		{
			//cout <<leksema<<endl;
			vf[count]=leksema;
			++count;
		}

		leksema=strtok(NULL,"\n");
	}
}

вообщем мы можем уже считать файл и получить #include

для начала сделаем проверку для одного файла:

int main(int argc,char* argv[])
{
	string f="ex09_1.cpp";

	if(argc>1)
	{
		char* vf[100]={0};
		for(int i=1;i<argc;i++)
		{
			//vf[100]={0};
			readF(argv[i],vf);

			//vuvod failov
			cout <<endl<<"*************  "<<argv[i]<<"  **********"<<endl;
			for(int i=0;vf[i]!=0;i++)
			{
				cout <<vf[i]<<endl;
			}
		}

		//obrabatuvaem macciv vf
		char* massH[200]={0};//massiv ykazatelei na zagolovochnue failu
		int count=0;
		for(int i=0;vf[i]!=0;i++)
		{
			char* leksema=strtok(vf[i]," ");
			leksema=strtok(NULL," ");
			//cout <<"leksema = "<<leksema<<endl;
			if(leksema[0]!='<')
			{
				massH[count]=new char[strlen(leksema)];
				strcpy(massH[count],leksema);
				count++;
			}
			//exit(1);
		}

		//vuvod macciva zagolovochnux failov
		for(int i=0;massH[i]!=0;i++)
		{
			cout <<massH[i]<<endl;
		}

		//cravnivaem ecli ravnu to vuvod coobcheni9 i vuxod iz programmu
		for(int i=0;massH[i]!=0;i++)
		{
			for(int j=i+1;massH[j]!=0;j++)
			{
				if(strcmp(massH[i],massH[j])==0)
				{
					cerr <<"povtornoe vklyuchenie "<<massH[i]<<endl;
					exit(1);
				}
			}
		}
	}

	return 0;
}

Для нескольких файлов нам всего лишь немного нужно изменить программу, а именно просканировать все файлы и сохранить в массив, а затем уже проверять

Вот что у меня получилось в конце, как бы программка сразу выводить одинаковые файлы по одному. (хотя можно было сделать просто вывод одинаковых файлов всех, либо в массив записать либо просто метку поставить, и если метка равна 0 то вывести Ок!, если метка не равна 0 то ясно Ок! выводиться не будет, а в цикле выведутся перед проверкой все одинаковые файлы. )

//programma chitaet icxodnue failu C++ i vuvodit cpicok vklyuchennux failov
#include <iostream>
using std::endl;
using std::cout;
using std::ios;
using std::cerr;
using std::cin;
#include <string>
using std::string;
#include <fstream>
using std::ifstream;
#include <cstdlib>
using std::exit;
#include <cstring>
using std::strtok;
#include <cctype>
using std::isspace;

//prinimaet nazvanie faila i massiv ctrok, return cpicok включаемых файлов
void readF(string f,char* vf[]);

int main(int argc,char* argv[])
{
	string f="ex09_1.cpp";

	if(argc>1)
	{
		char* massH[200]={0};//massiv ykazatelei na zagolovochnue failu
		int count=0;
		for(int i=1;i<argc;i++)
		{
			char* vf[100]={0};
			readF(argv[i],vf);

			//vuvod failov
			cout <<endl<<"*************  "<<argv[i]<<"  **********"<<endl;
			for(int i=0;vf[i]!=0;i++)
			{
				cout <<vf[i]<<endl;
			}
			//obrabatuvaem macciv vf		
			for(int i=0;vf[i]!=0;i++)
			{
				char* leksema=strtok(vf[i]," ");
				leksema=strtok(NULL," ");
				//cout <<"leksema = "<<leksema<<endl;
				if(leksema[0]!='<')
				{
					massH[count]=new char[strlen(leksema)];
					strcpy(massH[count],leksema);
					count++;
				}
				//exit(1);
			}
		}

		//vuvod macciva zagolovochnux failov
		for(int i=0;massH[i]!=0;i++)
		{
			cout <<massH[i]<<endl;
		}
		//exit(1);

		//cravnivaem ecli ravnu to vuvod coobcheni9 i vuxod iz programmu
		for(int i=0;massH[i]!=0;i++)
		{
			for(int j=i+1;massH[j]!=0;j++)
			{
				if(strcmp(massH[i],massH[j])==0)
				{
					cerr <<"povtornoe vklyuchenie "<<massH[i]<<endl;
					exit(1);
				}
			}
		}
	}

	return 0;
}

//prinimaet nazvanie faila i massiv ctrok, return cpicok включаемых файлов
void readF(string f,char* vf[])
{
	ifstream r(f.data(),ios::in);
	if(!r)
	{
		cerr <<"don't open file"<<endl;
		exit(1);//avar
	}

	char k;
	string result;
	while(r.get(k) !=0)
		result+=k;
	//cout <<result<<endl;
	//ichem includu
	const char* str=new char[result.size()];
	str=result.data();
	//cout <<str<<endl;
	char* leksema=strtok((char*)str,"\n");
	int count=0;
	while(leksema!=NULL)
	{
		//cout <<leksema<<endl;
		//poick include
		int flag=0;
		for(int i=0;leksema[i]!=0;i++)
		{
			if(!isspace(leksema[i])&&leksema[i]!='#')
				break;

			if(leksema[i]=='#')
			{
				if(leksema[i+1]=='i')
					if(leksema[i+2]=='n')
					{
						flag=1;
						break;
					}
			}
		}

		if(flag==1)
		{
			//cout <<leksema<<endl;
			vf[count]=leksema;
			++count;
		}

		leksema=strtok(NULL,"\n");
	}
}

Ну что же подведем итоги. Средство мы опробовали вроде как бы работает, если есть одинаковые файлы (заголовочные), то выводиться ошибка. Ну что ж ну так сказать не сильное средство морочное. Проще использовать средство контроля которое используют большинство описанное в главе 9.3.3, то то есть оформлять исходные файлы с использованием директив препроцессора #ifndef, #define, #endif

Обьясняю:

Допустим заголовочный файл hello.h оформлен в виде:

#ifndef HELLO_H
#define HELLO_H

//....
//сдесь находиться код заголовочного файла
//....

#endif

да если мы включим этот заголовочный файл в другой файл два раза, то содержимое файла прочитается всего один раз, потому, что препроцессор вначале увидит команду #ifndef HELLO_H это проверка определена ли константа HELLO_H, если она не определена, то препроцессор включит весь код который находиться между #ifndef HELLO_H и #endif

Во второй раз кода препроцессор встретит директиву #define HELLO_H он снова проверить определена ли HELLO_H, и на этот раз HELLO_H уже определена (ее определила команда в прошлом включении #define HELLO_H) и он проигнорирует все содержимое между #ifndef HELLO_H и #endif.

Вот так, то господа, как мне кажется естественно это средство намного лучше и проще не нужно морочить голову с оформлением, если есть, где нить повторные влючения, то не паришься, и все тупо работает. Главное правильно оформлять заголовочные файлы и проблем не будет.

Вообщем наша программка никаких дополнительных преимуществ нам не дает, токо зря время потратили 🙂

 

 

rss