Skip navigation

Category Archives: C++

Yesterday I started to poke around with the cxxtest testing framework.

So basically, to start right off you need:

  • Source file to test. It shouldn’t have a main function. At least if you do things the way I did which is by linking the compiled object file with the test runner object file (which defines a main function).
  • A header file which defines the actual test.
  • A Makefile that defines the rules needed to compile all of this.

Source files to test

So this is the simple source code that I am going to test (Yes I know it’s not a parser, I just named it that way, cause it’s cool):

#include "parser.hpp"

inline bool Parser::surrounded_by(const string& s, char left, char right)
{
  return s.at(0)==left && s.at(s.length()-1)==right;
}
 
void Parser::strip(string& s) {
  static const string filter="\r\n\t ";
 
  size_t right = s.find_last_not_of(filter);
  if(string::npos == right) {
    s.clear();
    return;
  }
 
  size_t left = s.find_first_not_of(filter);
  if(string::npos == left) left=0;
 
  s=s.substr(left,(right-left)+1);
}

And this is the header file:
#ifndef PARSER_HPP_
#define PARSER_HPP_
 
#include <string>
#include <map>
 
using std::map;
using std::string;
 
namespace Parser {
  inline bool surrounded_by(const string& s, char left, char right);
  void strip(string& s);
}
#endif

The actual test

I like things neatly tucked into directories so I made a subdir test. There I have a file named parserTest.h with the following content
Note: I don’t know why but WordPress displays the NULL character \ 0 (lines 51 and 54) as a space, so I wrote 0 instead.

#include <cxxtest/TestSuite.h>
#include "parser.hpp"
using std::string;

class ParserTestSuite : public CxxTest::TestSuite {
  public:
  void testStrip()
  {
    string s=" strip me  ";
    Parser::strip(s);
    TS_ASSERT_EQUALS(s,"strip me");
  }
  void testNoStrip()
  {
    string s="don't  strip me", s1=s;
    Parser::strip(s);
    TS_ASSERT_EQUALS(s,s1);
  }
  void testStripLeft()
  {
    string s="  left strip me";
    Parser::strip(s);
    TS_ASSERT_EQUALS(s,"left strip me");
  }
  void testStripRight()
  {
    string s="right strip me  ";
    Parser::strip(s);
    TS_ASSERT_EQUALS(s,"right strip me");
  }

  void testSurrondedBy()
  {
    const string s="?am I surronded by questionmarks?";
    TS_ASSERT( Parser::surrounded_by(s, '?', '?') );
  }
  void testNotSurroundedBy() {
    const string s="I'm the authority";
    TS_ASSERT( !Parser::surrounded_by(s, ' ', ' ') );
  }
  void testLeftOnlySurrondedBy() {
    const string s="?Left yes but right no.";
    TS_ASSERT( !Parser::surrounded_by(s, '?','!') );
  }
  void testRightOnlySurroundedBy() {
    const string s="Left no but right yes!";
    TS_ASSERT( !Parser::surrounded_by(s, '?', '!') );
  }
  void testNotSurroundedByNullChar() {
    const string s="Am I terminated by a null char? Are we all?";
    TS_ASSERT( !Parser::surrounded_by(s, 'A', 0) );
  }
  void testCStringNotSurroundedByNullChar() {
    TS_ASSERT( !Parser::surrounded_by("A C string", 'A', 0) );
  }
  void testCStringSurroundedBy() {
    TS_ASSERT( Parser::surrounded_by("! Yes !", '!', '!') );
  }

};

Of course this blog entry is just an example of how to basically use cxxtest. So you should check out the manual (html | pdf) (as always) to see much more info. Also in the cxxtest source tree there is a directory sample that holds Makefilse for different operating systems and a few basic tests. The main difference between my sample and the ones in the source code tree of cxxtest is that I have subdirectory that holds the tests, hence the Makefiile is a bit different (also, I’ve stripped some of it that I don’t need, like GUI output).

The Makefile

So, the Makefile is really important. Just put it in test/Makefile. Of course, you should edit the paths in the SHELL and TESTGEN directory. The TESTGEN variable holds the path to the Perl script that generates the test runner cpp file (called error_printer here) which is later compiled and run.

SHELL = /bin/bash
VPATH = ..:.
CXXC = g++ -Wall -I. -I..
CC = g++ -c -I. -I..
TESTGEN = /usr/bin/cxxtestgen.pl
TARGETS = error_printer
TESTS = *.h

all: $(TARGETS)
clean:
	rm -f *~ *.o *.obj $(TARGETS) $(GUI_TARGETS)
	rm -f tests.cpp error_printer.cpp
	rm -f qt_runner.cpp

distclean: clean
	rm -f Makefile

run: error_printer
	./error_printer

error_printer.cpp: $(TESTS)
	$(TESTGEN) -o $@ --error-printer $(TESTS)

stdio_printer.cpp: $(TESTS) parserTest
	$(TESTGEN) -o $@ --runner=StdioPrinter $(TESTS)

tests.cpp: $(TESTS)
	$(TESTGEN) -o $@ $(TESTS)

error_printer.o: error_printer.cpp
	$(CC) error_printer.cpp -o error_printer.o

error_printer: error_printer.o parser.o
	$(CXXC) error_printer.o parser.o -o error_printer

%: %.cpp
	$(CXXC) -o $@ $<

Now to run the tests we do
make run
in the test directory.

The output is:

[emil@lappy test]$ make run
/usr/bin/cxxtestgen.pl -o error_printer.cpp --error-printer *.h
g++ -c -I. -I.. error_printer.cpp -o error_printer.o
g++    -c -o parser.o ../parser.cpp
g++ -Wall -I. -I.. error_printer.o parser.o -o error_printer
./error_printer
Running 11 tests.........OK!

Област на действие (пространство от имена / namespace) е точно това, което подсказва името му: пространство, в което са дефинирани дадени имена (на функции, променливи, класове). Например, всеки клас създава в себе си ново пространство от имена:

//Област на действие-файл
using namespace std;

class Foo {
//Нова област на действие - област на действие клас (ОДК-така го съкращава Павлов явно, Бойлер тъп)
int bar();//bar - първи път

};
int bar;//обратно в област на действие-файл; bar-втори път
int main {

//Област на действие main

int bar;// bar - трети път

}
Трите имена bar са в 3 различни области на действие.
Операторът :: се използва за активиране на област на действие. Например:

  • int Foo::bar(), за да дефинираме извън класа Foo метода bar()
  • ::bar, за достъп до глобалната променлива (в област на действие-файл)

Когато един клас е дефиниран – да кажем имаме класа Foo, ние можем да си мислим за него като за нов тип. И освен да създаваме променливи от този нов тип (Foo thing;) имаме право да създаваме:

  • Указатели: Foo *p;
  • Масиви: Foo[10];
  • Псевдоними (references / препратки): Foo &same_thing=thing;

Ключовата дума static може да се разглежда в 3 смисъла, като следните 2 ни интересуват засега:
1. Статична променлива в тялото на функция.

int touch_me()
{
static int times=0; //локална статична променлива
cout << "You touched me " << ++times << " times! ";
}

Стойността на статичната променлива се инициализира при първото обръщение към функцията и се запазва между всеки 2 следващи (не се разрушава, както обикновената (auto / автоматична) локална променлива.

int main(void) {
touch_me(); // "You touched me 1 times! "
touch_me(); // "You touched me 2 times! "
}

Ако декларацията на променливата times във функцията touch_me() не беше static (т.е. auto – по default), променливата щеше да се разрушава след всяко извикване на функцията и да се създава при всяко ново извикване.

int main(void) {
touch_me(); // "You touched me 1 times! "
touch_me(); // "You touched me 1 times! "
}

2. Ключовата дума static в контекста на класовете.

В един клас може да имаме методи (член-функции / функции-елементи / member functions / methods) или променливи (член-променливи / променливи-елементи / member variables), които не са методи на обекта, а вместо това – на самия клас (от обекти).
2.1 Статични член-променливи.

class Animal {
static int count_animals;//Ето я статичната променлива!
string name;
Animal() {
count_animals++;
name='Unnamed';
}
};

int Animal::count_animals=0; //Приемам го на доверие. Така трябва да се инициализира статичната променлива на клас.

Така при всяко извикване на конструктора Animal::Animal(), т.е. при всяко създаване на нова променлива (инстанция/обект/instance/object) на класа Animal, статичната променлива ще се увеличава с 1 и следователно ще съдържа броя на създадените променливи от тип Animal.
2.2 Статични методи.
Това са методи на класа. Ето пример (прибавяме към горния клас):

static int get_animal_count() { return count_animals;}

Основните им свойства са:

  • Не се изисква да имаме обект на класа, за да направим обръщение към тях (да ги извикаме). Тоест обръщението е от вида Animal::get_animal_count();. Ако не беше статичен метод, щеше да ни трябва обект/инстанция на класа. Т.е. Animal new_animal; new_animal.get_animal_count();
  • Нямат this указател, т.е. не могат да използват член-променливи на класа, освен статичните такива.НО, имат пълен достъп до член-променливите на обекти от съответния клас!
  • class Human {
    int IQ;
    public:
    static void static_member_function {
    IQ=5; //Невалидно! Няма указател this, тъй като *статичният метод не е свързан с обект!*
    Human new_born;
    new_born.IQ=0; //Валидно!
    }
    };

Consider the following code:

int i;
int *pi = &i;

int i;
int &ri = i;

Both pi and ri contain addresses that point to the location of i, but the difference lies in
the appearance between references and pointers when they are used in expressions. In
order to assign a value of 4 to i in both cases, we write:

*pi = 4;

ri = 4;

Read More »

This is stupid, but self-explainable:

while ((cout << "Hint ")&&(cin >> foo)) {
//exploit foo
}

Follow

Get every new post delivered to your Inbox.