Poniżej przedstawiam zrefaktoryzwoany, przedstawiony wcześniej szkielet kodu aplikacji do przetwarzania tekstów (Labolatorium nr 15) . Aktualnie zaprezentowane zostały tematy związane z takimi zagadnieniami jak:
– biblioteka boost i wykorzystanie inteligentnych wskaźników (rozwiązanie problemów z ewentualnymi wyciekami pamięci np. przy wywoływaniu wyjątków),
– biblioteka standardowa STL,
– modyfikator const.
W miarę czasu postaram przedstawić zagadnienia związane z tampletami (ang. tamplates) stosowanymi do programowania ogólnego oraz wykorzystanie wyrażeń regularnych w budowaniu macierzy wyrażeń (ang. term matrix). Dobre wprowadzenie do biblioteki boost i wyrażeń regularnych zawiera Przemoc’s wiki. W dalszej kolejności planowane jest też pokazanie sposobów testowania kodu (ang. test unity) zamiast stosowania testów wewnątrz funkcji main().
Na koniec, a może nawet w następnym poście dotyczącym C/C++, chciałbym przedstawić zagadnienie związane z łączeniem PHP z C++. Maszyna zenda jest dość ciężka do opanowania (wiele pośrednich makr, dokumentacji etc.). Opanowanie jej zajmuje trochę czasu. Powstało jednakże bardziej elastyczne narzędzie w postaci SWIG w którym na pierwszy rzut oka zadanie to wydaje się o wiele elastyczniejsze i łatwiejsze niż poprzez hackowanie bezpośrednio zenda. Z rozwiązaniem tym pierwszy raz spotkałem się w Linux Magazine (artykuł pt. “PHP z C++”) a następnie na blogu IBMa Build PHP extensions with SWIG, gdzie Martin Streicherwyjaśnia w bardzo przystępny sposób jak pisać rozszerzenia używając C/C++ do PHP.
Kod zrefaktoryzwoanej aplikacji wykorzystujący utworzone interfejsy przedstawia listing poniżej:
//============================================================================
// Name : agencyText.cpp
// Author : Marcin Mirończuk
// Version : 1.0
// Copyright : GPL
// Description : The agency to text retrive
//============================================================================
/**
* Rejestracja bibliotek
*/
#include <iostream>
#include <string>
#include <vector>
#include "textAgent.h"
#include "boost/scoped_ptr.hpp"
/**
* Rejestracja przestrzeni nazw
*/
using namespace std;
using namespace ta;
using namespace boost;
/**
* Definicja typów
* Wstep do tworzenia szablonow @see http://pl.wikibooks.org/wiki/C++/Szablony_klas
*/
typedef ta::textAgent agentInt;
/**
* Program glowny
*/
int main(int argc, char** argv) {
/**
* Wystartowanie kilku agentow dynamicznych oraz statycznych, nazwanych jak i nienazwanych (anonimowych)
* Zastosowanie inteligentnych wskaznikow z biblioteki boost do zarzadzania czasem zycia obiektow
*/
agentInt agentStatyczny(1220, 1, "przetwarzacz tekstu 1", "Agent T1");
agentInt niezweryfikowanyAgentStatyczny;
boost::scoped_ptr<agentInt> agentDynamiczny(new agentInt(12330, 2, "przetwarzacz tekstu 2", "Agnet T2"));
boost::scoped_ptr<agentInt> niezweryfikowanyAgentDynamiczny(new agentInt());
try {
/**
* Pokazanie informacji o rezydujacych agentach
*/
cout << "Zarejestrowany agent i jego dane:" << endl;
agentStatyczny.showData();
cout << endl;
cout << "Agent oczekujacy na rejestracje:" << endl;
niezweryfikowanyAgentStatyczny.showData();
cout << endl;
cout << "Agent dynamiczny zarejstrowany:" << endl;
agentDynamiczny->showData();
cout << endl;
cout << "Agent dynamiczny niezweryfikowany:" << endl;
niezweryfikowanyAgentDynamiczny->showData();
cout << endl;
/**
* Rejestracja modulu (przybornika) do przetwarzania tekstow
* Dodatkowe testy przybornika
*/
tt::textTool tt;
tt::textTool ttTest;
cout << "Testowanie tollboksa do przetwarzania tekstu: " << endl;
ttTest.extractSegments("A ja mam kota. I wiecej tekstu.");
ttTest.showSegments();
cout << endl;
/**
* Rejestracja przybornika dla agenta dynamicznego
*/
agentDynamiczny->rejestrTextTool(tt);
agentDynamiczny->rejestrTextTool().extractSegments("Ala ma kota.");
cout << "Ilosc zliczonych segmentow: " << agentDynamiczny->rejestrTextTool().getCountSegments() << endl;
cout << "Pokaz jakie segmenty zliczyles: " << endl;
agentDynamiczny->rejestrTextTool().showSegments();
cout << endl;
/**
* Rejestracja przybornika dla agenta statycznego
*/
agentStatyczny.rejestrTextTool(tt);
agentStatyczny.rejestrTextTool().extractSegments("A ja mam kota. I wiecej tekstu.");
cout << "Ilosc zliczonych segmentow: " << agentStatyczny.rejestrTextTool().getCountSegments() << endl;
cout << "Pokaz jakie segmenty zliczyles: " << endl;
agentStatyczny.rejestrTextTool().showSegments();
cout << endl;
cout << "Testowanie usowania spacji:" << endl;
std::string testString1 = " kota ";
cout << "- Tekst przed filtracja: " << testString1 << endl;
cout << "- Filtracja: " << "Tekst odfiltrowany: " << agentStatyczny.rejestrTextTool().stripWhiteSpace(testString1) << endl;
cout << "- Filtracja: " << "Tekst pierwotny " << testString1 << endl;
std::string *pTestString1 = &testString1;
cout << "- Tekst przed filtracja wskaznik / zmienna: " << *pTestString1 << " / " << testString1 << endl;
agentStatyczny.rejestrTextTool().stripWhiteSpace(pTestString1);
cout << "- Filtracja: " << "Tekst odfiltrowany " << *pTestString1 << endl;
cout << "- Filtracja: " << "Tekst pierwotny " << testString1 << endl;
cout << endl;
cout << "Testowanie tokenizacji:" << endl;
std::string testString2 = "Ala ma kota. A kot ma na imie Ala.";
cout << "- Tekst podlegajacy tokenizacji: " << testString2 << endl;
agentStatyczny.rejestrTextTool().tokenize(testString2);
cout << "- Ilosc tokenow: " << agentStatyczny.rejestrTextTool().getCountTokens() << endl;
cout << "- Ilosc unikatowych tokenow: " << agentStatyczny.rejestrTextTool().getCountUniqueTokens();
cout << endl;
niezweryfikowanyAgentDynamiczny->rejestrTextTool();
} catch (std::string exception) {
cout << endl;
cout<<"Wyjatek: "<< exception;
}
return 0;
}
Interfejs agenta który rejestruje moduł do przetwarzania tekstu i za pośrednictwem którego odbywają się wszelkie modyfikacja przedstawia listing poniżej:
/*
* textAgent.h
*
* Created on: 21-12-2011
* Author: Marcin Mirończuk
* Licenc: GPL
*
* Klas modelujaca podstawowe elementy agenta do przetwarzania tekstu.
* Agent ma mozliwosc rejestrowania u siebie modulu do przetwarzania tekstu za omoca ktorego przetwarza tekst.
*
* @note::
* + Metody [nazwaMetody]get() const nie zmieniaja stanu obiektow na zecz ktorych sa wywolywane.
* (pola tylko do odczytu)
* + Czesc metod [nazwaMetody]get() moze byc wywolana inline
*
*/
#include <string>
#include "textTool.h"
#ifndef TA_H_
#define TA_H_
/**
* Definicja przestrzeni nazw
*/
namespace ta {
/**
* Definicja klasy
*/
class textAgent {
/**
* Skladowe i metody prywatne klasy
*/
private:
/**
* @type int timeCreate Czas w sekundach Unix timestamp
*/
int timeCreate;
/**
* @type float priority Priorytet agebta
*/
int priority;
/**
* @type string task Zadanie/praca do wykonania przez agebta
*/
std::string task;
/**
* @type string name Nazwa agenta
*/
std::string name;
/**
* @type bool isTextTool Flaga rejestracji modulu tekstu
*/
bool isTextTool;
/**
* @type textTool textTool Uchwyt do modulu przetwarzajacego tekst
*/
tt::textTool textTool;
/**
* Skladowe i metody publiczne klasy
*/
public:
/**
* Konstruktor podstawowy domyslny
*/
textAgent();
/**
* Konstruktor rozszerzony
*/
textAgent(int ptimeCreate, int ppriority, std::string ptask, std::string pname);
/**
* Pobranie wieku agenta
*
* @return float
*/
int getTimeCreate() const;
/**
* Pobranie wagi agenta
*
* @return float
*/
int getPriority() const;
/**
* Pobranie zadania agenta
*
* @return string
*/
std::string getTask() const;
/*
* Pobranie nazwy agenta
*
* @return string
*/
std::string getName() const;
/**
* Pokazanie danych o agencie
*
* @return void
*/
void showData() const;
/**
* Rejestracja modulu do przetwarzania tekstu
*
* @return tt::textTool
*/
const tt::textTool & rejestrTextTool(tt::textTool tt);
/**
* Rejestracja, pobranie modulu do przetwarzania tekstu.
*
* @return tt::textTool
* @thorw exception
*/
tt::textTool & rejestrTextTool() throw(std::string);
/**
* @see ta::textAgent::rejestrTextTool()
*
* @return tt::textTool
* @thorw exception
*/
tt::textTool & getTextTool();
/**
* Destruktor
*/
virtual ~textAgent();
};
} /* namespace textagent */
#endif /* TA_H_ */
Interfejs programu (modułu) do przetwarzania strumienia danych tekstowych przedstawia listing poniżej:
/*
* textAgent.cpp
*
* Created on: 21-12-2011
* Author: Marcin Mirończuk
* Licenc: GPL
*
* Implementacja interfejsu agenta do przetwarzania tekstu @see textAgent.h
*
*/
#include <iostream>
#include "textAgent.h"
#include "textTool.h"
namespace ta {
textAgent::textAgent() {
this->timeCreate = 0;
this->priority = 0;
this->task = "unknown";
this->name = "inkognito";
this->isTextTool = false;
}
textAgent::textAgent(int ptimeCreate, int ppriority, std::string ptask, std::string pname) {
this->timeCreate = ptimeCreate;
this->priority = ppriority;
this->task = ptask;
this->name = pname;
this->isTextTool = false;
}
int textAgent::getTimeCreate() const {
return this->timeCreate;
}
int textAgent::getPriority() const {
return this->priority;
}
std::string textAgent::getTask() const {
return this->task;
}
std::string textAgent::getName() const {
return this->name;
}
void textAgent::showData() const {
std::cout << "Time create: " << this->timeCreate << std::endl;
std::cout << "Priority: " << this->priority << std::endl;
std::cout << "Name: " << this->name << std::endl;
std::cout << "Task: " << this->task << std::endl;
}
const tt::textTool & textAgent::rejestrTextTool(tt::textTool tt) {
if(this->isTextTool == false) {
this->textTool = tt;
this->isTextTool = true;
return this->textTool;
} else {
return this->textTool;
}
}
tt::textTool & textAgent::rejestrTextTool() throw(std::string) {
if(this->isTextTool == false) {
std::string exception = "Unregister text tool!";
throw exception;
} else {
return this->textTool;
}
}
tt::textTool & textAgent::getTextTool() {
return this->rejestrTextTool();
}
textAgent::~textAgent() {
}
} /* namespace ta */
Implementację interfejsu programu do przetwarzania tekstu zawiera listing poniżej:
/*
* textTool.h
*
* Created on: 21-12-2011
* Author: Marcin Mirończuk
* Licenc: GPL
*
* Klas modelujaca modul (przybornik) do przetwarzania tekstu.
*
* @note::
* + Metody [nazwaMetody]get() const nie zmieniaja stanu obiektow na rzecz ktorych sa wywolywane.
* (pola tylko do odczytu)
* + Czesc metod [nazwaMetody]get() moze byc wywolana inline
* + Zabezpieczenie funkcji zwracajacych referencje modyfikatorem const tj.
* Uniemozliwienie wywolan typu ttTest.extractSegments("") = temp;
* @see http://rab.ict.pwr.wroc.pl/~kreczmer/po/materialy/referencje.pdf
*
*/
#include <map>
#include <vector>
#include <string>
#ifndef TT_H_
#define TT_H_
/**
* Definicja przestrzeni nazw
*/
namespace tt {
/**
* Definicja klasy
*/
class textTool {
/**
* Skladowe i metody prywatne klasy
*/
private:
/**
* @type vector<string> segment Wektor segmentow
*/
std::vector<std::string> segments;
/**
* @type vector<string> tokens Wektor tokenow (wyrazen)
*/
std::vector<std::string> tokens;
/**
* @type vector<string> uniqueTokens Wektor unikalnych tokenow (wyrazen)
*/
std::vector<std::string> uniqueTokens;
/**
* @type vector<string, int> tokensFrequ Tablica klucz=wartosc czestotliwosci wyrazen
*/
std::map<std::string, int> tokensFrequ;
/**
* Usowanie duplikatow wyrazen z wektora wyrazen
*
* @see http://learningcppisfun.blogspot.com/2008/04/remove-duplicates-from-vector.html
* @see http://stackoverflow.com/questions/1041620/most-efficient-way-to-erase-duplicates-and-sort-a-c-vector
*
* @param vector<string> vec
*
* @return vector<string>
*/
const std::vector<std::string> & removeDuplicates(std::vector<std::string> vec);
public:
/**
* Domyslny konstruktor
*/
textTool();
/**
* Wydobywanie segmentow z tekstu.
*
* @param string &text
*
* @return vector<string>
*/
const std::vector<std::string> & extractSegments(const std::string &text);
/**
* Pobranie ilosci wydobytych segmentow z tekstu.
*
* @return int
*/
int getCountSegments() const;
/**
* Pokazanie wydobytych segmentow z tekstu.
*
* @return void
*/
void showSegments() const;
/**
* Usuniecie spacji z poczatku jak i z konca lancucha znakow.
*
* @param string text
*
* @return string
*/
std::string stripWhiteSpace(std::string text);
/**
* Przeciazona wersja usuniecia spacji z poczatku jak i z konca lancucha znakow.
*
* @param string *text
*
* @return void
*/
void stripWhiteSpace(std::string *text);
/**
* Kalkulacja, wyznaczanie macierzy czetotliwosci wystepowania wyrazen w pojedynczym dokumencie tekstowym.
*
* @param string &text
*
* @return map<string, int>
*/
const std::map<std::string, int> & computeTokensFrequ(const std::string &text);
/**
* Tokenizacja tekstu tj. rozbicie tekstu na wektor wyrazen.
* @see http://oopweb.com/CPP/Documents/CPPHOWTO/Volume/C++Programming-HOWTO-7.html
*
* @param string &text
*
* @return vector<string>
*/
const std::vector<std::string> & tokenize(const std::string &text);
/**
* Pobranie ilosci wyrazen.
*
* @return int
*/
int getCountTokens() const;
/**
* Pobranie ilosci unikalnych wyrazen.
*
* @return int
*/
int getCountUniqueTokens();
/**
* Domyslny destruktor
*/
virtual ~textTool();
};
} /* namespace tt */
#endif /* TT_H_ */
Implementację powyżej zaprezentowanego interfejsu programu do przetwarzania tekstu zawiera listing poniżej:
/*
* textTool.cpp
*
* Created on: 21-12-2011
* Author: Marcin Mirończuk
* Licenc: GPL
*
* Implementacja interfejsu modulu do przetwarzania tekstu @see textTool.h
*
*/
#include <iostream>
#include <vector>
#include <string>
#include <sstream>
#include <algorithm>
#include "textTool.h"
namespace tt {
textTool::textTool() {
}
/**
* Bardzo naiwna, prezentacyja wersji algorytmu do rozbijania tekstu na segmenty (zdania)
*/
const std::vector<std::string> & textTool::extractSegments(const std::string &text) {
std::string tempSegment;
for(int i = 0; i < text.length(); i++) {
if(text[i] == '.' || text[i] == '?' || text[i] == '!') {
tempSegment = (std::string) tempSegment;
this->segments.push_back(this->stripWhiteSpace(tempSegment));
tempSegment.clear();
} else {
tempSegment += text[i];
}
}
return this->segments;
}
int textTool::getCountSegments() const {
return this->segments.size();
}
std::string textTool::stripWhiteSpace(std::string text) {
if(text[0] == ' ') {
text.replace(0, 1, "");
}
if(text[text.length()-1] == ' ') {
text.replace(text.length()-1, 1, "");
}
return text;
}
void textTool::stripWhiteSpace(std::string *text) {
std::string::iterator it;
it = text->begin();
if(*it == ' ') {
text->replace(0, 1, "");
}
it = text->end();
if(*(it-1) == ' ') {
text->replace(text->length()-1, 1, "");
}
}
void textTool::showSegments() const {
for(int i = 0; i < this->segments.size(); i++) {
std::cout << this->segments[i] << std::endl;
}
}
const std::map<std::string, int> & textTool::computeTokensFrequ(const std::string &text) {
this->tokenize(text);
this->removeDuplicates(this->tokens);
std::vector<std::string>::iterator it;
//@todo :: Zaimplementowac algorytm do tworzenia macierzy wyrazen (term matrix, tm)
for (it = this->uniqueTokens.begin(); it < this->uniqueTokens.end(); it++) {
std::cout << " " << *it;
}
return this->tokensFrequ;
}
const std::vector<std::string> & textTool::tokenize(const std::string &text) {
// Buforowany string
std::string buf;
// Wstawienie stringu z potoku
std::stringstream ss(text);
while (ss >> buf) {
this->tokens.push_back(buf);
}
return this->tokens;
}
int textTool::getCountTokens() const {
return this->tokens.size();
}
int textTool::getCountUniqueTokens() {
if(this->tokens.size() != 0 && this->uniqueTokens.size() == 0) {
this->removeDuplicates(this->tokens);
return this->uniqueTokens.size();
} else {
return this->uniqueTokens.size();
}
}
const std::vector<std::string> & textTool::removeDuplicates(std::vector<std::string> vec) {
std::sort(vec.begin(), vec.end());
vec.erase(std::unique(vec.begin(), vec.end()), vec.end());
this->uniqueTokens = vec;
return this->uniqueTokens;
}
textTool::~textTool() {
}
} /* namespace tt */