How to set end condition of string iterator in C++? - string

i'm new in C++. I met a problem when doing exercise in C++ Primer 5th edition(Ex 9.43). The for loop can't stop in my function find_and_replace. Here is the code:
#include <iostream>
#include <string>
int find_and_replace(std::string& org_str, const std::string& str4find, const std::string& str4replace);
int main(int argc, char const *argv[])
{
std::string str("I am a very loooooong string to be process!");
int find_times;
find_times = find_and_replace(str, "a", "###");
std::cout << find_times << std::endl;
std::cout << str << std::endl;
return 0;
}
int find_and_replace(std::string& org_str, const std::string& str4find, const std::string& str4replace)
{
int find_times = 0;
if (org_str.size() < str4find.size())
{
std::cout << "Error: The original string is too short!" << std::endl;
return find_times;
}
else if (org_str == str4find)
{
org_str.assign(str4replace);
++find_times;
return find_times;
}
for (auto i = org_str.begin(), j = i + str4find.size(); j != org_str.end(); )
{
std::string temp(i, j);
// std::cout << temp << std::endl;
if (temp == str4find)
{
j = org_str.erase(i, j);
org_str.insert(i, str4replace.begin(), str4replace.end());
// std::cout << org_str << std::endl;
// std::cout << *i << std::endl;
j = i + str4find.size();
// std::cout << *j << std::endl;
++find_times;
}
else
{
++i;
++j;
}
}
if (org_str.substr(org_str.size() - str4find.size()) == str4find)
{
org_str.erase(org_str.size() - str4find.size(), str4find.size());
org_str.insert(org_str.end(), str4replace.begin(), str4replace.end());
++find_times;
}
return find_times;
}
I update iterator j and i after erase and insert operation, so i think, i and j are not invalid.
Anyone can tell me why the end condition: j != org_str.end() is not work?
SOLVED!
Although the cout output is correct, the i iterator is already invalid. Here is the new code. Just change few lines in for-loop.
#include <iostream>
#include <string>
int find_and_replace(std::string& org_str, const std::string& str4find, const std::string& str4replace);
int main(int argc, char const *argv[])
{
std::string str("I am a very loooooong string to be process!");
int find_times;
find_times = find_and_replace(str, "a", "###");
std::cout << find_times << std::endl;
std::cout << str << std::endl;
return 0;
}
int find_and_replace(std::string& org_str, const std::string& str4find, const std::string& str4replace)
{
int find_times = 0;
if (org_str.size() < str4find.size())
{
std::cout << "Error: The original string is too short!" << std::endl;
return find_times;
}
else if (org_str == str4find)
{
org_str.assign(str4replace);
++find_times;
return find_times;
}
for (auto i = org_str.begin(), j = i + str4find.size(); j != org_str.end(); )
{
std::string temp(i, j);
// std::cout << temp << std::endl;
if (temp == str4find)
{
i = org_str.erase(i, j);
//CHANGE BEGIN
//org_str.insert(i, str4replace.begin(), str4replace.end());
for (auto k = str4replace.begin(); k != str4replace.end(); ++k, ++i)
{
i = org_str.insert(i, *k);
}
//CHANGE END
// std::cout << org_str << std::endl;
// std::cout << *i << std::endl;
j = i + str4find.size();
// std::cout << *j << std::endl;
++find_times;
}
else
{
++i;
++j;
}
}
if (org_str.substr(org_str.size() - str4find.size()) == str4find)
{
org_str.erase(org_str.size() - str4find.size(), str4find.size());
org_str.insert(org_str.end(), str4replace.begin(), str4replace.end());
++find_times;

The i iterator become invalid after calling erase and insert, you should use the return values of that functions or recalculate the position starting from s.begin() again.
#include <iostream>
#include <string>
using std::cout;
using std::string;
int find_and_replace( string &s, const string &oldVal, const string &newVal ) {
int find_times = 0;
if ( s.size() < oldVal.size() ) {
std::cout << "Error: The original string is too short!" << std::endl;
return find_times;
}
if ( s == oldVal ) {
s.assign(newVal);
return ++find_times;
}
for ( auto i = s.begin(); i != s.end(); ++i ) {
// compare using iterators
bool is_equal = true;
auto k = i;
for ( auto j = oldVal.begin(); j != oldVal.end(); ++j, ++k ) {
if ( k == s.end() || *j != *k ) {
is_equal = false;
break;
}
}
if ( is_equal ) {
auto ie = s.erase(i, i + oldVal.size());
// This may not work with older version of g++
auto ii = s.insert(ie, std::begin(newVal), std::end(newVal));
i = ii + newVal.size() - 1;
++find_times;
}
}
return find_times;
}
int main() {
string str("I am a very loooooong string to be process!");
int find_times;
find_times = find_and_replace(str, "a", "###");
cout << find_times << '\n';
cout << str << '\n';
return 0;
}
The output (as you can see here) is:
2
I ###m ### very loooooong string to be process!

Related

why do i get this compile error: " error: cannot bind non-const lvalue reference of type ‘Matrix&’ to an rvalue of type ‘Matrix’ "?

Ok so i'am writting this code for an exercise we have to write a class for matrices and then to check it we use a code that the teacher wrote to check if it performes the normal operations.
And i don't get exactly where i am wrong it seems to bother the compiler that i return a type Matrix but really i dont get what is a lvalue or rvalue .
But i get this when i try to compile
anthony#anthony-linux:~/Documents/CS/Ex6$ g++ -Wall simplematrixtest.cc Matrix.cc -o simplematrixtest
simplematrixtest.cc: In function ‘int main()’:
simplematrixtest.cc:70:24: error: cannot bind non-const lvalue reference of type ‘Matrix&’ to an rvalue of type ‘Matrix’
70 | Matrix g = f.multiply(a);
| ~~~~~~~~~~^~~
In file included from simplematrixtest.cc:4:
Matrix.hh:13:18: note: initializing argument 1 of ‘Matrix::Matrix(Matrix&)’
13 | Matrix(Matrix &m);
| ~~~~~~~~^
this is my class Matrix:
#include "Matrix.hh"
#include <cmath>
#include <iostream>
using namespace std;
// Default constructor: initializes the Matrix.
Matrix::Matrix() {
nrows=0;
ncols=0;
values=0;
}
// Initializes the Matrix with rows and cols.
Matrix::Matrix(int numberOfRows,int numberOfCols) {
nrows=numberOfRows;
ncols=numberOfCols;
values = new double[nrows*ncols] ;
for (int i = 0; i < nrows*ncols; i++) {
values[i] = 0;
}
}
// Copy constructor
Matrix::Matrix(Matrix &m)
{
nrows=m.getRows();
ncols=m.getCols();
int sizeOfM=m.getRows()*m.getCols();
values = new double[sizeOfM];
for (int i = 0; i < sizeOfM; ++i) {
values[i] = m.values[i];
}
}
// Destructor - Matrix allocates no dynamic resources.
Matrix::~Matrix() {
delete[] values;
// no-op
}
// Mutators:
void Matrix::setElem(int rowIndex,int colIndex,double val){
values[rowIndex*ncols+colIndex]=val; /*See Jasper i listen to what you say ! No return for a void function :) */
}
void Matrix::subtract(Matrix &m) {
for (int i = 0; i < nrows*ncols; i++) {
values[i] -=m.values[i];
}
}
void Matrix::add(Matrix &m) {
for (int i = 0; i < nrows*ncols; i++) {
values[i] +=m.values[i];
}
}
Matrix Matrix::multiply(Matrix &m) {
if(ncols!=m.getRows()){
cout << " Error :invalid matrix multiplication"<< endl;
Matrix ret(0,0);
return ret;
}
else{
Matrix ret(nrows,m.getCols());
for (int rowIndex = 0; rowIndex < ret.nrows; rowIndex++) {
for (int colIndex = 0; colIndex < ret.ncols; colIndex++) {
for(int count=0; count <= ncols; count++) {
ret.values[rowIndex*ncols+colIndex] +=(values[rowIndex*ncols+(count)]*m.values[count*ncols+colIndex]);
}
}
}
return ret;
}
}
// Accessors:
int Matrix::getRows() {
return nrows;
}
int Matrix::getCols() {
return ncols;
}
double Matrix::getElem(int rowIndex,int colIndex){
return values[rowIndex*ncols+colIndex];
}
bool Matrix::equals(Matrix &m) {
if(m.getRows()!=nrows||m.getCols()!=ncols){return false;}
for (int i = 0; i < nrows*ncols; i++) {
if(values[i] !=m.values[i]){return false;}
}
return true;
}
Apparently the problem is in multiply here is the test code
#include <iostream>
using namespace std;
#include "Matrix.hh"
// Have some fun playing with a Matrix!
int main(void) {
// Create a new matrix with two rows and three columns
Matrix a(2, 3);
// Print out some information about the size of this matrix:
cout << "Matrix a is " << a.getRows() << " rows tall." << endl;
cout << "Matrix a is " << a.getCols() << " columns wide." << endl;
// Print out the contents of this matrix (should be all zeroes!):
for(int r = 0; r < a.getRows(); r++) {
for(int c = 0; c < a.getCols(); c++) {
cout << "Element (" << r << ", " << c << ") = " << a.getElem(r,c) << endl;
}
}
// Fill in a few values
a.setElem(1, 2, -5280); // bottom right element is now -5280
a.setElem(0, 1, 123); // top center element is now 123
// Create an identical copy of this matrix
Matrix b = a;
// Change the original matrix some more
a.setElem(1, 2, 555); // bottom right element is now 555
// Examine some elements of each matrix
cout << "(1,2) of a = " << a.getElem(1,2) << " [should be 555]" << endl;
cout << "(1,2) of b = " << b.getElem(1,2) << " [should be -5280]" << endl;
// So if a and b are different, let's copy a into a new matrix and add b to it:
Matrix c = a;
c.add(b);
// Now let's copy c into another new matrix, d, and subtract a from it:
Matrix d = c;
d.subtract(a);
// Hmm... that means d should be b, no?
if (d.equals(b)) {
cout << "Yay! d = b!" << endl;
} else {
cout << "Uh-oh! Something went wrong, d isn't b!" << endl;
}
// Let's create a tiny 0 by 0 matrix using the default constructor:
Matrix e;
cout << "0x0 matrix e is " << e.getRows() << " by " << e.getCols() << endl;
// Of course, e and d are different, since they have different sizes!
if(!e.equals(d)) {
cout << "e and d are indeed different!" << endl;
} else {
cout << "Oh well, back to the drawing board...." << endl;
}
Matrix f(2,2);
f.setElem(0, 0, 2.0);
f.setElem(1, 0, 3.0);
f.setElem(0, 1, 5.0);
f.setElem(1, 1, 7.0);
Matrix g = f.multiply(a);
if (g.getElem(1, 2) == 3885.0) {
cout << "Multiply seems to work!" << endl;
} else {
cout << "Error in multiply() !" << endl;
}
// Okay, enough of this; destroy all those matrices and end the program:
return 0;
}
If i write
Matrix g = a ;
f.multiply(a);
The code compiles without any problem so it is a problem with this line: Matrix g =f.multiply(a);

getting response only on the first time with socket programming

I'm trying to create an HTTP server using C++ 98.
The issue is that, every time I launch my server, I get the response, sending again the request from the same browser tab, and the browser keeps loading.
In my kqueue, it seems like the block for checking the read event is not being executed on the second time.
This is my code:
void webserv::Server::lunch(){
this->kq.create_event(this->sock.getSocket(), EVFILT_READ);
while(1)
this->_lunch_worker();
}
void webserv::Server::_lunch_worker(void)
{
int n_ev;
int accept_sock;
int address_size;
char buff[1000];
webserv::Header header;
// register the events in ev_list
std::cout << GREEN << "---- WAITING FOR CONNECTION ----" << RESET << std::endl;
n_ev = this->kq.get_event();
for (int i = 0; i < n_ev; i++)
{
if (this->kq.get_fd(i) < 0)
continue;
if (this->kq.get_fd(i) == this->sock.getSocket())
{
std::cout << "--- RECEIVED NEW CONNECTION ---" << std::endl;
address_size = sizeof(this->sock.getAddress());
accept_sock = accept(
this->sock.getSocket(),
(struct sockaddr*)&this->sock.getAddress(),
(socklen_t *)&address_size
);
this->sock.test_error(accept_sock);
this->kq.create_event(accept_sock, EVFILT_READ);
int flags;
if ((flags = fcntl(accept_sock, F_GETFL, 0)) < 0) {
perror("fcntl");
close(accept_sock);
close(this->sock.getSocket());
}
if (fcntl(accept_sock, F_SETFL, flags | O_NONBLOCK) < 0) {
perror("fcntl");
close(accept_sock);
close(this->sock.getSocket());
}
this->kq.create_event(accept_sock, EVFILT_WRITE, EV_ADD | EV_ONESHOT);
}
else if (this->kq.is_read_available(i))
{
int bytes_read;
std::cout << "START: is_read_available" << std::endl;
if ((bytes_read = recv(this->kq.get_fd(i), buff, 999, 0)) > 0)
{
}
}
else if (this->kq.is_write_available(i))
{
std::string hello = "HTTP/1.1 200 OK\nContent-Type: text/plain\nContent-Length: 12\n\nHello world!";
if (send(this->kq.get_fd(i), hello.c_str(), hello.length(), 0) != 0)
{
std::cout << "TEST2" << std::endl;
this->kq.set_event(this->kq.get_fd(i), EVFILT_WRITE, EV_DELETE);
this->kq.get_event_list()[i].ident = -1;
close(this->kq.get_fd(i));
}
std::cout << "END: is_write_available" << std::endl;
}
}
}
And this is the kqueue class:
/************************ CONSTRUCTORS/DESTRUCTOR ************************/
webserv::Kqueue::Kqueue()
{
this->_kq = kqueue();
std::cout << "KQUEUE CREATED" << std::endl;
this->test_error(this->_kq, "Creating Kqueue :");
this->_n_ev = 0;
}
webserv::Kqueue::~Kqueue()
{
close(this->_kq);
}
/************************ MEMBER FUNCTIONS ************************/
void webserv::Kqueue::set_event(int fd, int filter, int flags, void *udata)
{
EV_SET(&this->_ev_set, fd, filter, flags, 0, 0, udata);
}
void webserv::Kqueue::add_event(void)
{
int ret;
ret = kevent(this->_kq, &this->_ev_set, 1, NULL, 0, NULL);
this->test_error(ret, "Kqueue/add_even functions");
}
int webserv::Kqueue::get_event(void)
{
this->_n_ev = kevent(this->_kq, NULL, 0, this->_ev_list, __EV_LIST_SIZE__, NULL);
this->test_error(this->_n_ev, "Kqueue/get_event function:");
return (this->_n_ev);
}
void webserv::Kqueue::create_event(int fd, int filter, int flags, void *udata)
{
this->set_event(fd, filter, flags, udata);
this->add_event();
}
bool webserv::Kqueue::isEOF(int index)
{
if (this->_n_ev <= index)
this->test_error(-1, "Kqueue/isEOF function:");
return (this->_ev_list[index].flags & EV_EOF);
}
bool webserv::Kqueue::is_read_available(int index)
{
if (this->_n_ev <= index)
this->test_error(-1, "Kqueue/is_read_available function:");
return (this->_ev_list[index].filter == EVFILT_READ);
}
bool webserv::Kqueue::is_write_available(int index)
{
if (this->_n_ev <= index)
this->test_error(-1, "Kqueue/is_write_available function:");
return (this->_ev_list[index].filter == EVFILT_WRITE);
}
void webserv::Kqueue::test_error(int fd, const std::string &str)
{
if (fd < 0)
{
std::cerr << RED << str << " ";
perror("The following error occured: ");
std::cerr << RESET;
exit(EXIT_FAILURE);
}
}
/************************ GETTERS/SETTERS ************************/
struct kevent *webserv::Kqueue::get_event_list()
{
return (this->_ev_list);
}
int webserv::Kqueue::get_fd(int index)
{
if (this->_n_ev <= index)
this->test_error(-1, "Kqueue/get_ev_list function:");
return (this->_ev_list[index].ident);
}
void webserv::Kqueue::set_kqueue(int fd)
{
this->_kq = fd;
}

How to debug Run-Time Check Failure #2 - Stack around the variable 'newString' was corrupted

This is a function to convert a long string (sentence) into a vector of strings. From the error, it seems obvious that the char ptr newString is causing some access violation, run-time issues. Probably it's length is the root cause?
Also, please note that the output of the function is just coming as expected, but with the run-time error occurring.
vector<string> convertStrgToStrVect(char *inString)
{
unsigned int end = 0, start = 0, i = 0, len = 0, j = 0;
char *inStr = inString, *s;
char newString[] = "";
vector<unsigned int> startLenVect, endLenVect, strLenVect;
vector<unsigned int> :: iterator itr1, itr2;
vector<string> stringVect;
vector<string> :: iterator itr3;
s = inStr;
// Add an extra space an the end of the string
for( i = 0; i < strlen(s); i++)
{
}
s[i] = ' ';
i++;
s[i] = '\0';
cout << s << endl;
cout << strlen(s) << endl;
// Create vectors for start and end indexes to split the words separated by a space
for( i = 0; i < strlen(s); i++)
{
if(s[i] != ' ')
{
end++;
len = end - start;
}
else
{
endLenVect.push_back(end);
startLenVect.push_back(start);
strLenVect.push_back(len);
end = i+1;
start = i+1;
}
}
cout << "startLenVect: ";
for(itr1 = startLenVect.begin(); itr1 != startLenVect.end(); itr1++)
{
cout << *itr1 << " ";
}
cout << "endLenVect: ";
for(itr1 = endLenVect.begin(); itr1 != endLenVect.end(); itr1++)
{
cout << *itr1 << " " << endl;
}
for(itr1 = startLenVect.begin(), itr2 = endLenVect.begin(); itr1 != startLenVect.end(); itr1++, itr2++)
{
strcpy(newString, "");
for(i = *itr1, j = 0; i < *itr2; i++, j++)
{
newString[j] = s[i];
}
newString[j] = '\0';
stringVect.push_back(newString);
}
for(itr3 = stringVect.begin(); itr3 != stringVect.end(); itr3++)
{
cout << "stringVect: " << *itr3 << endl;
}
return stringVect;
}

std::thread context of execution (c++14)

The problem appear when an in/out variable of a function called by std::thread changes the value during the execution...
Function:
static int func(stThread_t *&pStThread)
Parameters
pStThread: It´s a struct that has a pointer to std::thread and other variables (some flags)
typedef struct stThread {
stThread() noexcept {...};
stThread(const stThread &cRigth) noexcept {...};
stThread & operator = (const stThread &cRigth) noexcept {...};
std::thread *pThread;
volatile bool bBegin;
volatile bool bEnd;
} stThread_t;
The function func print the address of the std::thread of the parameter
pStThread and the thread id
func before 1785280 this_id 21968
after making an this_thread::sleep for 2 seconds, it print it again
func afer ... this_id 21968
static int func(stThread_t *&pStThread) {
std::thread::id this_id = std::this_thread::get_id();
long long p_begin = (long long)pStThread;
std::cout << "func before " << std::to_string(p_begin) << " this_id " << this_id << "\n";
std::cout.flush();
pStThread->bBegin = true;
std::this_thread::sleep_for(std::chrono::milliseconds(2000));
this_id = std::this_thread::get_id();
long long p_end = (long long)pStThread;
std::cout << "func afer " << std::to_string(p_end) << " this_id " << this_id << "\n";
std::cout.flush();
pStThread->bEnd = true;
return 1;
};
The address of the pointer to std::thread it´s changed (corrutped, deleted..?)
The pStThread is pushing_back of a list of pointer struct stThread_t
std::list<stThread_t*> listOfThreads;
listOfThreads.push_back(pStThread);
I read about std::move, but does not work with pointers
At the end there is a thread "garbage collector" that it is trying to erase all the threads pending of execution.
Full code here
#include <string>
#include <list>
#include <vector>
#include <map>
#include <thread>
#include <mutex>
#include <atomic>
#include <iostream>
typedef struct stThread {
stThread() noexcept {
pThread = NULL;
bBegin = false;
bEnd = false;
};
stThread(const stThread &cRigth) noexcept {
this->pThread = cRigth.pThread;
this->bBegin = (bool)cRigth.bBegin;
this->bEnd = (bool)cRigth.bEnd;
};
stThread & operator = (const stThread &cRigth) noexcept {
this->pThread = cRigth.pThread;
this->bBegin = (bool)cRigth.bBegin;
this->bEnd = (bool)cRigth.bEnd;
return *this;
};
std::thread *pThread;
volatile bool bBegin;
volatile bool bEnd;
} stThread_t;
class CMain
{
public:
typedef std::list<stThread_t*> MyList_threads;
MyList_threads listOfThreads;
public:
CMain() {
std::cout << std::boolalpha << "Ex1 is move-constructible? "
<< std::is_move_constructible<stThread_t>::value << '\n'
<< "Ex1 is trivially move-constructible? "
<< std::is_trivially_move_constructible<stThread_t>::value << '\n'
<< "Ex1 is nothrow move-constructible? "
<< std::is_nothrow_move_constructible<stThread_t>::value << '\n'
<< "Ex2 is trivially move-constructible? "
<< std::is_trivially_move_constructible<stThread_t>::value << '\n'
<< "Ex2 is nothrow move-constructible? "
<< std::is_nothrow_move_constructible<stThread_t>::value << '\n';
};
static int func(stThread_t *&pStThread) {
std::thread::id this_id = std::this_thread::get_id();
long long p_begin = (long long)pStThread;
std::cout << "func before " << std::to_string(p_begin) << " this_id " << this_id << "\n";
std::cout.flush();
pStThread->bBegin = true;
std::this_thread::sleep_for(std::chrono::milliseconds(2000));
this_id = std::this_thread::get_id();
long long p_end = (long long)pStThread;
std::cout << "func afer " << std::to_string(p_end) << " this_id " << this_id << "\n";
std::cout.flush();
pStThread->bEnd = true;
return 1;
};
int _createThreads() {
for (int iIdx = 0; (iIdx < 5); iIdx++) {
stThread_t *pStThread = new stThread_t;
pStThread->pThread = new std::thread(&CMain::func,
std::ref(pStThread));
if (pStThread) {
do {
std::this_thread::sleep_for(std::chrono::milliseconds(100));
} while (!pStThread->bBegin);
listOfThreads.push_back(pStThread);
std::string sLog;
sLog = "\nlistOfThreads.push_back " + std::to_string((long long)pStThread) + "\n";
std::cout << sLog;
std::cout.flush();
}
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
return 1;
};
int _main() {
_createThreads();
std::thread thread_collector([=]() {
bool bEnd = false;
MyList_threads::iterator it;
it = listOfThreads.end();
do {
stThread_t *pStThread = NULL;
if (it == listOfThreads.end()) {
it = listOfThreads.begin();
if (it == listOfThreads.end()) bEnd = true;
}
else it++;
if (it != listOfThreads.end()) {
if ((*it)->bEnd) {
pStThread = *it;
listOfThreads.erase(it);
it = listOfThreads.begin();
}
}
if (pStThread) {
if (pStThread->pThread) {
if (pStThread->pThread->joinable()) {
pStThread->pThread->join();
std::cout << " element deleted " << std::to_string((long long)pStThread) << "\n";
std::cout.flush();
}
delete pStThread->pThread;
pStThread->pThread = NULL;
}
delete pStThread;
}
pStThread = NULL;
std::this_thread::sleep_for(std::chrono::milliseconds(1));
} while (!bEnd);
});
if (thread_collector.joinable()) {
thread_collector.join();
}
return 1;
};
};
int main()
{
CMain _main;
_main._main();
return 0;
}
You have a rather straightforward bug, that is mostly unrelated to threading:
(1) func takes a reference to an stThread_t*.
static int func(stThread_t *&pStThread);
(2) You pass in a reference to pStThread
std::thread(&CMain::func,std::ref(pStThread));
(3) Which is a local variable whose lifetime ends as soon as the loop iteration is finished
for (int iIdx = 0; (iIdx < 5); iIdx++) {
stThread_t *pStThread = new stThread_t;
//...
}
(4) And consequently, you get undefined behavior when your function tries to access the object after it is destroyed. (be careful! here, "the object" refers to the pointer in question, not the object the pointer is pointing to)
It's unclear why you insist on passing pStThread by reference; your function doesn't actually modify the pointer (just what's pointed to), and you don't seem to intend to do any of the things that such a device is actually good for.

How to get a glyph outline of a true type character on a linux system

I'm searching for a library to get the outline of a glyph in a true type font on a linux system. We are using Pango and Cairo but unfortunatly I didn't find anything.
I'm looking for somethig similar to GlyphTypeface.GetGlyphOutline under .NET
Any help or hints are appreciated!
Thanks in advance
The solution is to use FreeType, it offers the range of function I need:
#include <string>
#include <iostream>
#include <freetype/ftglyph.h>
#include <freetype/freetype.h>
//******************* check error code ********************
void Check(FT_Error ErrCode, const char* OKMsg, const char* ErrMsg)
{
if(ErrCode != 0)
{
std::cout << ErrMsg << ": " << ErrCode << "\n";
std::cout << "program halted\n";
exit(1);
}
else
std::cout << OKMsg << "\n";
}
//******************** get outline ************************
int GetOutLine(FT_Glyph glyph, FT_OutlineGlyph* Outg)
{
int Err = 0;
switch ( glyph->format )
{
case FT_GLYPH_FORMAT_BITMAP:
Err = 1;
break;
case FT_GLYPH_FORMAT_OUTLINE:
*Outg = (FT_OutlineGlyph)glyph;
break;
default:
;
}
return Err;
}
//*********** print outline to console ***************
int PrintOutLine(FT_OutlineGlyph Outg)
{
int Err = 0;
FT_Outline* Ol = &Outg->outline;
int Start = 0; //start index of contour
int End = 0; //end index of contour
short* pContEnd = Ol->contours; //pointer to contour end
FT_Vector* pPoint = Ol->points; //pointer to outline point
char* pFlag = Ol->tags; //pointer to flag
for(int c = 0; c < Ol->n_contours; c++)
{
std::cout << "\nContour " << c << ":\n";
End = *pContEnd++;
for(int p = Start; p <= End; p++)
{
char Ch = *pFlag++ + '0';
std::cout << "Point " << p <<": X=" << pPoint->x << " Y="<<pPoint->y << " Flag=" << Ch << "\n";
pPoint++;
}
Start = End + 1;
}
return Err;
}
//*********** get glyph index from command line *************
FT_UInt GetGlyphIndex(int argc, char* argv[], int Nr)
{
if(argc > Nr)
{
return atoi(argv[Nr]);
}
else
{
return 36;
}
}
//*********** get font name from command line *************
void GetFontName(int argc, char* argv[], int Nr, std::string& FontName)
{
if(argc > Nr)
{
FontName += argv[Nr];
}
else
{
FontName += "FreeMono.ttf";
}
}
//*********** get font size from command line *************
int GetFontSize(int argc, char* argv[], int Nr)
{
short FontSize = 50 * 64;
if(argc > Nr)
FontSize += atoi(argv[Nr]);
return FontSize;
}
//******************** M A I N ************************
// par1: FontName, par2:Glyph-Nr, par3: FontSize as FT_F26Dot6
int main(int argc, char* argv[])
{
FT_Face face;
FT_Library library;
FT_Error error;
error = FT_Init_FreeType( &library );
Check(error, "", "error initializing FT lib");
std::string FontName;
FontName = "/usr/share/fonts/truetype/freefont/";
GetFontName(argc, argv, 1, FontName);
error = FT_New_Face( library, FontName.c_str(), 0, &face );
Check(error, "", "error loading font");
FT_F26Dot6 font_size = GetFontSize(argc, argv, 3);
error = FT_Set_Char_Size( face, font_size, font_size, 72, 72 );
Check(error, "", "error setting char size");
FT_UInt glyph_index = GetGlyphIndex(argc, argv, 2);
FT_Int32 load_flags = FT_LOAD_DEFAULT;
error = FT_Load_Glyph( face, glyph_index, load_flags );
Check(error, "", "error loading glyph");
FT_Glyph glyph;
error = FT_Get_Glyph( face->glyph, &glyph );
Check(error, "", "error getting glyph");
FT_OutlineGlyph Outg;
error = GetOutLine(glyph, &Outg);
Check(error,"", "error getting outline");
std::cout << "=======================================================\n";
std::cout << "Font: " << FontName << "\n";
std::cout << "Glyph Index: " << glyph_index << "\n";
std::cout << "=======================================================\n";
error = PrintOutLine(Outg);
Check(error,"", "error printing outline");
return 0;
}
Cairo has this capability builtin.
cairo_glyph_path() will get the glyph outline and make it the current path, and allow you to get the current path and iterate over it.

Resources