Code Style

Комментарии к заданию про XML

Условие задания

Задание: найти максимальную длину текста между тэгами в байтах.

Предположим, у нас был исходный файл

 <p> текст с выделенным <b> словом </b> </p>

Каждый открывающий и закрывающий тэг заканчивает кусок текста, поэтому в данном примере ответ - это длина текста "текст с выделенным ".
Что делает characterDataHandler
В задании про XML необходимо было использовать фунцию characterDataHandler(void* userData, const XML_Char* s, int len), чтобы подсчитать количество символов между тэгами.

Функция characterDataHandler(void* userData, const XML_Char* s, int len) вызывается не для текста между тэгами. Неизвестно, сколько раз вызовется эта функция между двумя последовательными тэгами. SAX хендлеры используют константную память, поэтому если между тэгами достаточно большой текст без переводов строк, то функция вызовется не один раз.

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

Минусы глобальных переменных:
- засоряем пространство имен
- невозможно использовать парсер для двух документов. Если хотим использовать для нескольких документов, то, после каждого использования, надо аккуратно обнулять документы.
- непонятно, что получится, если запустить парсер на двух документах параллельно.

Не используйте статические переменные

Избавление от глобальных переменных с помощью статических не решает всех проблем, а решает только проблему с засорением пространства имен.

 
static size_t temp

Не надо "прятать" static переменные в функциях. Это не решает ни одной проблемы, а только уменьшает понятность кода. Становится сложнее отловить возможные проблемы со static переменнными. Т.е. не стоит писать:

 
void characterDataHandler(void *userData,const XML_Char *s,int len){
  static size_t temp;
  ...
}
Используйте struct или class
В заданиии про XML надо было использовать класс:
 
class XMLCounterData{
private:
  size_t TagCounter;
  size_t AttributeCounter;
  size_t MaxTextLength;
  size_t CurrentTextLength;
};
static void charDataHandler(void *userData,const XML_Char *s,int len){
  XMLCounterData& counter = *(XMLCounterData*)userData;
  counter.MaxTextLength = ...
}
А в main пишем:
 
  XML_Parser first = XML_ParserCreate(NULL);
  count a;
  XML_SetUserData(first,&a);
  XML_SetElementHandler(first, start, end);
  XML_SetCharacterDataHandler(first, charDataHandler); 
Прием: статические функции класса вместо просто статических функций

Вариант решения задачи с помощью статической функции класса:

 
class XMLCounterData{
private:
  size_t TagCounter;
  size_t AttributeCounter;
  size_t MaxTextLength;
  size_t CurrentTextLength;
public:
  static void charDataHandler(void *userData,const XML_Char *s,int len);
};
 
void XMLCounterData::charDataHandler(void *userData,const XML_Char *s,int len){
  XMLCounterData& counter = *(XMLCounterData*)userData;
  counter.MaxTextLength = ...
}
Чтобы создать парсер и установить CharDataHandler в main() пишем:
 
  XML_Parser first = XML_ParserCreate(NULL);
  count a;
  XML_SetUserData(first,&a);
  XML_SetElementHandler(first, start, end);
  XML_SetCharacterDataHandler(first, a.charDataHandler); 
Этот вариант лучше тем, что мы не засоряем пространство имен в нашем файле и становится понятнее к какой структуре относится хэндлер.

Понятность кода

Форматируйте код
Не пишите загадочный код
В каком порядке объявлять поля и методы класса
В каком порядке стоит писать private,public,protected в объявлении класса.
В начале стоит писать то, что будут использовать пользователи, т.е. общие методы, а только потом поля. Но до методов следует ввести статические переменные
Возможный порядок:
static методы
static поля
конструкторы
деструкторы
public методы
private методы
поля
запрещенные конструкторы

Не бойтесь писать несколько private подряд.

Не забывайте писать объявление friend функции

friend foo() не является объявлением функции foo(). Пример:

 
class Complex{
  private:
	int myRe;
	int myIm;
  public:
	Complex(int re = 0, int im = 0);
	friend std::ostream& operator<<(std::ostream& out, const Complex a);	// не является объявлением
};
std::ostream& operator<<(std::ostream& out, const Complex a); // объявление
Не дублируйте код
Способы избавления от повторяющегося кода:
Потенциальные проблемы с headers
- Не включайте header в другие header, т.к. процесс компиляции становится длиннее
- Включайте сначала системные заголовочные файлы, а потом собственные. Рассмотрим пример: пусть в файле foo.h есть описание функции printf(int) и мы включили сначала stdio.h, а потом наш заголовочный файл. Тогда нам компилятор выдаст ошибку, что в каком-то системном файле, в какой-то строчке есть ошибка. Довольно проблематично найти строку, в которой произошла ошибка. А если мы включим заголовочные файлы в обратном порядке, то ошибка будет найдена в нашем заголовочном файле, соответственно будет легче разобраться в причинах ошибки.
- Не используйте using namespace std. Любой системный заголовочный файл может включать другие заголовочные файлы, а в разных версиях библиотек могут включаться разные заголовочные файлы в данный header. В связи с чем, может на одном компьютере может не быть ошибки коллизии обозначений, а на другом, она может появиться. Пример:
 
using namespace std;
 
struct count{
  int tag_count;
  int attr_count;
  int max_len;
  int temp;
  count(){tag_count =0; attr_count = 0; max_len = 0; temp = 0;}; 
};
 
static void start(void *data, const char *el, const char **attr){
  count* c =(count*) data;
  (*c).tag_count += 1 ;
   for (int i = 0; attr[i]; i += 2) {
     (*c).attr_count += 1;
  }  
} 
В функции start выдавалась ошибка, которая отсылала в какие-то системные файлы. Оказалось, что уже существует функция count().