Tuesday, October 2, 2007

C++ struct initialization

---- / from boost.asio example / ----
struct mapping
{
const char* extension;
const char* mime_type;
} mappings[] =
{
{ "gif", "image/gif" },
{ "htm", "text/html" },
{ "html", "text/html" },
{ "jpg", "image/jpeg" },
{ "png", "image/png" },
{ "swf", "application/x-shockwave-flash" },
{ 0, 0 } // Marks end of list.
};

Thursday, September 27, 2007

Andrei Alexandrescu

Оригинал статьи:
http://www.ddj.com/cpp/184403766

Ключевое слово volatile было разработано для отключения компиляторной оптимизации, которая могла бы привести к неверной работе кода в многопоточном окружении. К примеру: если переменная базового типа объявлена как volatile, то компилятору не разрешается кэшировать ее в регистре - распостраненная оптимизация, которая может привести к катастрофическим результатам, если данная переменная используется в нескольких потоках. Так что общее правило - если у вас есть переменные базовых типов, которые нужно использовать в нескольких потоках - объявляйте их как volatile. Однако возможности volatile намного шире: вы можете использовать его для нахождения не thread-safe кода и делать это можно в compile time. В статье показано как это сделать; в решении используется простой smart pointer, который также облегчает сериализацию критических секций кода.

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

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

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

Всего лишь небольшое ключевое слово
Although both C and C++ Standards are conspicuously silent when it comes to threads, they do make a little concession to multithreading, in the form of the volatile keyword.

Just like its better-known counterpart const, volatile is a type modifier. It's intended to be used in conjunction with variables that are accessed and modified in different threads. Basically, without volatile, either writing multithreaded programs becomes impossible, or the compiler wastes vast optimization opportunities. An explanation is in order.

Рассмотрим следующий код:
class Gadget
{
public:
void Wait()
{
while (!flag_)
{
Sleep(1000); // sleeps for 1000 milliseconds
}
}
void Wakeup()
{
flag_ = true;
}
...
private:
bool flag_;
};

The purpose of Gadget::Wait above is to check the flag_ member variable every second and return when that variable has been set to true by another thread. At least that's what its programmer intended, but, alas, Wait is incorrect. Suppose the compiler figures out that Sleep(1000) is a call into an external library that cannot possibly modify the member variable flag_. Then the compiler concludes that it can cache flag_ in a register and use that register instead of accessing the slower on-board memory. This is an excellent optimization for single-threaded code, but in this case, it harms correctness: after you call Wait for some Gadget object, although another thread calls Wakeup, Wait will loop forever. This is because the change of flag_ will not be reflected in the register that caches flag_. The optimization is too ... optimistic. Caching variables in registers is a very valuable optimization that applies most of the time, so it would be a pity to waste it. C and C++ give you the chance to explicitly disable such caching. If you use the volatile modifier on a variable, the compiler won't cache that variable in registers — each access will hit the actual memory location of that variable. So all you have to do to make Gadget's Wait/Wakeup combo work is to qualify flag_ appropriately:
class Gadget
{
public:
... as above ...
private:
volatile bool flag_;
};

Most explanations of the rationale and usage of volatile stop here and advise you to volatile-qualify the primitive types that you use in multiple threads. However, there is much more you can do with volatile, because it is part of C++'s wonderful type system.


Перевод: Andrew Selivanov for crossplatform.ru

Wednesday, September 12, 2007

GoF Adapter pattern implementation

Slightly modified version from: http://www.cppblog.com/cxl82116/archive/2007/04/21/22513.aspx

//============================================================================
// Name : Adapter.cpp
// Author : Andrew Selivanov
// Version : 1.0
// Copyright : Andrew Selivanov
// Description : GoF Adapter pattern implementation
//============================================================================

#include <iostream>

#define USE_ADAPTER
#ifndef USE_ADAPTER

class OrginSystem
{

public:
void Supply()
{
std::cout<<"Orgin:SupplyACup"<<std::endl;
}
};
class CapSystem
{
public:
void Supply()
{
std::cout<<"CapSystem:SupplyACap"<<std::endl;
}
};
class Adapter: public OrginSystem , CapSystem
{
public:
void Supply()
{
OrginSystem::Supply();
CapSystem::Supply();
std::cout<<"So the request has been satisfied!"<<std::endl;
}
};

int main()
{
Adapter adapter;
adapter.Supply();
}
#else

class OrginalSystem
{

public:

virtual void Supply()
{
std::cout<<"Orgin:SupplyACup"<<std::endl;
}
};
class CapSystem
{
public:
virtual void Supply()
{
std::cout<<"CapSystem:SupplyACap"<<std::endl;
}
};
class Adapter: public OrginalSystem , CapSystem
{
OrginalSystem* _os;
CapSystem* _cs;

public:
Adapter():_os(new OrginalSystem),_cs(new CapSystem)
{

}
void Supply()
{
_os->Supply();
_cs->Supply();
std::cout<<"So the request has been satisfied!"<<std::endl;
}
};

int main()
{
OrginalSystem* original = new Adapter();
original->Supply();
delete original;
}

#endif

Wednesday, August 22, 2007

How to parse *.ini files with boost

I've spend some time to explore how to use "config_file_iterator" from boost::program_options library. Here is example:
---  config_test.cpp  ---
// Snippet 1: Boost config file parsing
//
#include <iostream>
#include <string>
#include <set>
#include <sstream>

#include <boost/config.hpp>
#include <boost/program_options/detail/config_file.hpp>
#include <boost/program_options/parsers.hpp>

namespace pod = boost::program_options::detail;

int main()
{
//contents
std::stringstream s(
"a = 1\n"
"b = 2\n"
"c = test option\n");
//parameters
std::set<std::string> options;
options.insert("a");
options.insert("b");
options.insert("c");

//parser
for (pod::config_file_iterator i(s, options), e ; i != e; ++i)
{
std::cout << i->value[0] << std::endl;
}
}
------------------




And more detailed sample:
#include <iostream>
#include <string>
#include <set>
#include <sstream>
#include <exception>
#include <fstream>

#include <boost/config.hpp>
#include <boost/program_options/detail/config_file.hpp>
#include <boost/program_options/parsers.hpp>

namespace pod = boost::program_options::detail;

int main()
{
std::ifstream config("manager.ini");
if(!config)
{
std::cerr<<"error"<<std::endl;
return 1;
}

//parameters
std::set<std::string> options;
std::map<std::string, std::string> parameters;
options.insert("*");

try
{
for (pod::config_file_iterator i(config, options), e ; i != e; ++i)
{
std::cout << i->string_key <<" "<<i->value[0] << std::endl;
parameters[i->string_key] = i->value[0];
}
std::cout << parameters["StatLogServer.Path"] << std::endl;
}
catch(std::exception& e)
{
std::cerr<<"Exception: "<<e.what()<<std::endl;
}
}

Thursday, August 9, 2007

multiple definition error, cpp

Say you have *.h file with header guards (see below):

#ifndef SOME_HEADER_
#define SOME_HEADER_

int a; //it leads to multiple definition error, because the header guards protect
//multiple inclusion of a header file in a single translation unit
//if you include this file in several translation units then the error
//will occur
//To prevent this, you should move the definition in some implementation file
//or make it inline/static

#endif /*SOME_HEADER_*/

And another one thing on header guards:
//__SOME_HEADER_ - WRONG: two leading underscores, implementation reserved
// _SOME_HEADER_ - WRONG: one leading underscore, implementation reserved
// SOME_HEADER_ - RIGHT:

Wednesday, July 4, 2007

Interesting books to read #1

Technical
  • Secrets of the C++ Masters by Jeff Alger
  • C++ for Real Programmers by Jeff Alger
  • Design Patterns: Elements of Reusable Object-Oriented Software by Erich Gamma et al


Linguistics
  • Corpora in Applied Linguistics by Susan Hanston


Other
  • How to write a thesis by Rowena Murray - excellent book,  must read

Saturday, June 9, 2007

Makefile for multiple binaries

CC = gcc.exe
BIN = test01.exe \
test02.exe \
test03.exe

OBJ = $(BIN:.exe=.o)
CXXFLAGS = -lstdcxx -lm
RM = del

compile: $(BIN)

$(BIN): %.exe : %.o
$(CC) $< -o $@ $(CXXFLAGS)

$(OBJ): %.o : %.cpp
$(CC) -o $@ -c $< $(CXXFLAGS)

clean:
del *.exe del *.o