tisdag 18 maj 2010

Minimizing build times for C++ projects under VS2005 and later

I recently looked over our build times in our now quite large C++ project. They have slowly been creeping towards the unacceptable, but after some tweeking they are now about a third of what they were. Just wanted to list what I did here. Some things are specific for Visual Studio (we're using 2005) but most will work on any IDE.

  • Use precompiled header. I think precompiled headers have an undeserved bad reputation. If they are used correctly they can reduce your compile time tremendously, especially if you use template libraries to any extent (like STL and boost). Any big, static header file should go with the precompiled stuff (windows.h, winsock, and all stl/boost headers are obvious candidates). Check out this link on how to get it right.
  • Minimize unnessecary includes. Our project was full of unnessesary includes in header files, mostly of the nature that they could be replaced with a forward declaration. Forward declaration really is your friend! However, you should never be tempted to violate the standard rule that "Every header file should be self-sufficient". That is, a header file should not be dependent of includes made by a header file which itself includes.
  • Set the Debug Information Format to your needs. I usually use /Zi, which does not include information for Edit and Continue like the default setting. This actually makes a big difference in the size of the database files, and therefore reduces compile time. Of course, if you actually do frequently use Edit and Contiune, you should use the default.
  • Define WIN32_LEAN_AND_MEAN. This strips away seldomly used parts of windows.h, which is a monster to compile (and should be placed with the precompiled headers). It is a small chance that you will actually need anything that this defines strips away.
  • Use the /MP switch in Visual Studio. Actually, in VS2005 this is an undocumented switch that has to be added to the "Additional command line arguments" field under C++ settings. It lets the compiler use more than one core during compilation. Just activating this switch on my dual core machine reduced the total build time with about 30 %.
  • Get yourself a SSD disk. This one speaks for itself. The problem is just to persuade your IT department that you need one.

söndag 1 november 2009

Building STLPort for Windows CE with Visual Studio 2005

As we want to use Boost in our WinCE project, we needed a full implementation of STL (including streams support). I never got Microsofts STL implementation to build for our device, probably because of some difference in the C library on our SDK. Therefore I decided to use STLPort which is designed to be more portable.

The documentation on STLPort is far from comprehensive, and it took me a great deal of time until I found the right information in the docs and on the forum to build the thing. Below is the script that sets up all needed enviornment variables and configures STLPort.

It should work for other versions of MSVC as well. Just change all the set to point to the right directories. I am not sure the use-boost is really needed

@echo off
set STLROOT=C:\STLport-5.2.1
cd %STL_ROOT%


set OSVERSION=WCE500
set PLATFORM=BACX
set ARCH=ARMV4I
set SDKROOT=C:\workspace\Alpha\main\SDK\wce500
set VSROOT=C:\Program Files\Microsoft Visual Studio 8
set BOOSTROOT=C:\Program Files\Boost\boost_1_40_0


call "%VSROOT%\VC\vcvarsall.bat"

set PATH=%VSROOT%\VC\ce\bin;%VSROOT%\VC\ce\bin\x86_arm;%VSROOT%\VC\bin;%VSROOT%\Common7\IDE;;%PATH%


set INCLUDE=%SDKROOT%\%PLATFORM%\include\%ARCH%;%SDKROOT%\%PLATFORM%\ATL\include;;%INCLUDE%


set LIB=%SDKROOT%\%PLATFORM%\lib\%ARCH%;%SDKROOT%\%PLATFORM%\ATL\lib\%ARCH%;%VSROOT%\VC\ce\lib\%ARCH%;;%LIB%


set CC=cl.exe
set TARGETCPU=ARMV4I
set CFG=none

call configure evc8 -x --with-static-rtl --use-boost %BOOSTROOT%

cd %STL_ROOT%/build/lib
nmake install

tisdag 22 september 2009

Invoking Properties in .NET

I just stumbled across a problem recently in a Windows Form application I was writing in C#. I wanted to make an update to the GUI from another thread, using Invoke(). The problem was that the update included setting a Property, and not a normal method call. And .NET (at least 2.0, I don't know about fancy 3.5 or upcoming 4.0) don't support Setting and Getting Properties.

This might seem strange, as setting a property is really only making a method call. However, there are two common solutions for this.

The first is to simply write a method that sets the property, and then Invoke() this method. However, I had plenty of classes and places where I had to set Properties, so this was not an ideal solution.

The other solution, which I ended up using, is to use Reflection to get an MethodInfo object of the Property's set. You can then use that object to create a delegate dynamically. In the example, I set the class own SetID property to "4".:


public delegate void test(int i);

public partial class Form1 : Form
{
private int setId = 0;

public int SetID
{
set
{
setId = value;
}
}

void anotherThread()
{
MethodInfo mi = this.GetType().GetProperty("SetID").GetSetMethod();
Delegate del = Delegate.CreateDelegate(typeof(test), this, mi);
this.Invoke(del, 4);
}
}

lördag 15 augusti 2009

All programmering är inte webprogrammering

I dessa dagar hör man oftare och oftare folk (mest webprogrammerare och självutnämnda evangelister) som påstår att applikationer i form av exekverbara filer snart kommer att vara helt borta, ersatta av webbapplikationer.

Visst, det finns en stark trend att användarapplikationer mer eller mindre helt håller på att flytta till webben. Men vad alla dessa männiksor glömmer är att bara en del av världens kodbas ligger i sådana program.

Jeff Atwood, som annars ofta säger vettiga saker, gick så långt som att säga att:

"Pretty soon, all programming will be web programming".

Har Jeff måhända aldrig hört talas om:
  • Automation och maskinkontroll? Jag har inga direkta siffror, men den sammanlagda kodbasen i alla världens fabriker passerar nog långt dagens webbapplikationer. Detta är ofta kritiska realtids-system som absolut aldrig skulle passa på webben.
  • Simulering och beräkning? Också något som nästan alla fabriker använder sig av, samt forskare och labbpersonal världen över. Beräkningstungt och minimal nytta att flytta till webben förutom möjligtvis viss presentation.
  • Spel? Tunga 3D-spel kommer alltid att kräva extremt optimerade exekverbara filer i grunden (även om de kanske inte exekveras lokalt).
  • Embedded-mjukvara? Allt från klockor, bankomater, kortläsare, tunnelbanespärrar, sjukhusutrustning, till avancerade telefoner och handdatorer använder inbyggd mjukvara som självklart inte alls lämpar sig att flytta till webben.
  • Drivrutiner? Inget behöver tilläggas här.
  • Annat? Unika system som t ex hela världens mobilnät (allt från radiointerface till samtalsrouting), världens fasta telefonnät, satteliter, trafikövervakning, etc, etc?
Det är bara att inse, folk som tror att all programmering kommer att vara webbprogrammering är antingen inte insatta i dagens digitala samhälle, eller så är de önsketänkare. Och tur är väl det, för webbprogrammering är ju så extremt trist.

tisdag 9 juni 2009

Från hanterad kod till C++, del 4

Detta är del 3 i inläggsserien om att gå från .NET/Java till C++. Förra delen handlade om Boost, det bästa tillägget till standard C++ där ute. Denna del ska handla om kodkonventioner. C++ lider av problemet att det är extremt fritt, och det är lätt att göra subtila felsteg som skapar stora problem senare.

Detta inlägg presenterar därför 9 stycken konventioner man bör följa då man programmerar C++. Alla dessa är tagna från den fantastiska boken "C++ Coding Standards" av Sutter & Alexandrescu, som innehåller 101 "best practices", med lite modifierade exempel och mycket mindre text.

Jag har valt ut de 9 jag finner mest intressanta och användbara, men utelämnat de mest basala (som t ex, att man alltid ska skriva include guards, något som de flesta IDE:er sköter åt automatiskt en nuförtiden, eller att man ska minimera global data).

Använd const proaktivt (punkt 15)
Jag har tidigare pratat om på hur många sätt const kan användas. Även om det är jobbigt att tänka efter precis när man bör ha en const-deklaration skapar det mycket robust kod, speciellt då man skriver SDKer och APIer för andra programmerare.

const är framförallt viktigt att använda för parametrar och returvärden då man hanterar pekare och referenser, och som regel ska man alltid returnera ett const pekare om man vill att anroparen inte ska kunna ändra den.

const har förmågan att propagera genom koden; om man deklarerar const för en medlem eller funktion slutar det ofta att man måste deklarera anropade parametrar och funktioner const. Detta är dock en feature.

Exempel. Det rätta sättet att skicka in en sträng (som inte ska ändras) till en funktion är:

void func(const std::string &str);

Vi deklarerar den som en referens för att slippa onödig kopiering, men som const för att försäkra oss att ingen modifikation av strängen sker.

Minimera cykliska beroenden (punk 22)
Det är väldigt lätt att råka skriva kod där två header-filer inkluderar varanndra, antingen direkt eller genom en cirkel. Detta tyder ofta på att designen i koden är dålig, och även om kompilatorn kan klara av detta (vilket vissa kanske tycker är direkt fel) så avråds man från detta.

Ändock värre är om man hamnar i ett cykliskt beroende med libs eller dller. Detta är extremt dålig design, då ett bibliotek ska symbolisera en oberoende modul, och två bibliotek som beror på varanndra betyder att de egentligen är en enda modul.

Ta parametrar som värden, referenser eller pekare på rätt sätt (punkt 25)
Om man är ny till C++ kan det vara komplicerat att på direkten se hur man bör skicka parametrar till funktioner. Ovan visades ett exempel med const och referens. Sutter, Alexandrescus har några tips:
  • const:a alltid pekare och referenser till input-parametrar
  • Ta värden främst som primitiva typer och sådana typer som är billiga att kopiera
  • Skicka som referens om funktionen inte tänker spara en pekare till argumentet.
Föredra en medlem istället för arv (punkt 34)
Jag tycker att denna punkt är väldigt intressant då jag ser många som använder arv alldelles för flitigt. I många fall är det bättre att bädda in en medlem av en klass i en annan klass (som privat medlem) istället för att skapa en klasshirearki via arv. Du har mycket bättre kontroll över hur klassen kallas, du får kortare kompileringstider, och du får inte lika nära koppling.

Man ska t ex komma ihåg att många klasser inte alls är tänkta att ärva från, speciellt gäller detta nästan alla klasser i std.

Deklarera basklassers destruktorer public och virtual, eller protected och icke-virtuella (punkt 50)
Detta är en ganska subtil punkt som kan orsaka väldigt konstiga runtimefel om man glömmer den. Säg att man deklarerar klasses Base:s destruktor public. I klassen Derived som ärver från Base deklarerar man ingen destruktor. I följande kod:


Derived *d = new Derived();
Base *b = (Base*)d;
delete b;


kommer vi att få ett odefinierat beteende, då Base desturktor anropas, men då b egenligen pekar till en objekt av Derived.

Om Base:s destruktor hade varit virtuell, hade istället Derived:s destruktor kallats, vilket är beteendet vi är ute efter.

Tillåt eller förbjud kopiering explicit (punkt 53)
C++ kompilatorer lägger automatiskt till en copy constructor (och en copy assignment constructor) om man själv inte definierar någon. Detta är ofta bra, men det finns många klasser som aldrig någonsin är tänkta att kopieras. Exempelvis finns det ingen vettig mening med att kopiera en klass som representerar en nätverksförbindelse (som ju binder upp en systemresurs).

För att inte andra ska göra misstaget att råka kopiera din klass är det bäst att explicit förbjuda detta. Det finns olika sätt, men jag föredrar att låta klassen som inte ska kunna kopieras ärva (privat) från boost::noncopyable. Ett annat sätt är att deklarera copy konstruktorerna privat.

Kasta som värde, fånga som referens (punkt 73)
Detta är standardsättet att kasta och fånga undantag på i C++, något som inte är lätt att veta som nybörjare. För det mesta ska man också fånga som const:

try
{
if (fails)
throw MyException();
}
catch (const MyException &e)
{
cout << "Problem"; }


Använd std::vector och std::string::c_str() för att prata med icke-C++ APIer (punkt 78)
För att kommunicera med externa APIer, skrivna i t ex C, behöver man inte transformera alla sina std::vector till arrayer, som många tror. En std::vector fungerar från C sett precis som en array, där första elementet pekar till början på std::vector. Och en std::string som man kallar c_str() på returnerar en C-lik null-terminerad char*.

Föredra algoritmanrop framför handskrivna loopar (punk 84)
Det är lätt att av gammal vana skriva en egen loop för att göra en operation på en kontainer. Dock så finns det massor med fina funktioner i STL och i samarbete med boost::bind kan man skriva många operationer som one-liners, med mycket lägre risk för fel.

Ett exempel: Jag ville nyligen hitta ett object av typen Exercise i en vector där dess medlem std::string name matchade ett namn jag ville söka på, name. Detta går ju att skriva för hand, men denna lösning blir mycket snyggare och säkrare:


std::vector::iterator it = std::find_if(exercises.begin(), exercises.end(), boost::bind(&ExerciseManager::compare, _1, name));


Och i samma klass definieras compare-funktionen:


bool ExerciseManager::compare(Exercise ex, const std::string &name)
{
if (name.compare(ex.name))
return false;
else
return true;
}

Efter anropet kommer iteratorn it (magiskt?) att peka på första elementet med matchande namn. Läs på om STL:s sök - och sorteringsalgoritmer och om boost::bind, om du inte redan gjort det!

lördag 23 maj 2009

Sydkoreaner är coola!

Jag har alltid beundrat sydkoreaner. I dagens DN står det om ex-presidenten som hoppat ut för en klippa då han blivit anklagad för att ha tagit emot mutor.

Detta är precis den bergssäkra vilja som byggt upp denna fantastiska nation. Män som Boxer visade ingen nervositet inför en Starcraft-match, och hans ansiktsuttryck skönjde inte en känsla, oavsett om han vann eller förlorarde. Man kan säga att Boxer och ex-presidenten är två sidor av samma mynt. Snacka om riktiga män!

onsdag 13 maj 2009

Medierna och datorerna

Idag läste jag DN:s artikel "Illegal fildelning minskar stort", den senaste i en lång rad artiklar relaterade till Piratebayrättegången, fildelning och nätneutralitet. Det slog mig då, visserligen inte för första gången, hur extremt stor makt media har över vilken information gemene man har tillgång till.

Jag kommer ihåg hur det var i början av 90-talet. På denna tid hade nog inte en ppm av den vuxna befolkningen koll på vad piratkopiering, Internet eller neddladdning var. Och det fascinerande är att det på den tiden redan existerade en hel datakultur bland yngre folk.

Till exempel, då Doom släpptes 1993 laddades första sharewareversionen hem av 10 miljoner människor (ungefär lika många som spelar WoW idag). Dataspelsindustrin i USA var redan en mångmiljardindustri. Det fanns redan stora grupper som släppte spel på BBS:er och Internet, som t ex Razor 1911, och det hölls ett antal stora rättsliga processer mot fildelarsiter.

I vanlig media såg man dock inte ett knyst om något av detta. Att försöka förklara för någon att man sysslade gaming, nerladdning eller att chatta på IRC var näst intill omöjligt. Och om folk förstod tyckte dom att det var lite udda. Det var en gigantisk ungdomskultur som aldrig uppmärksammades av folk över 25 år.

Varför tog det då sådan tid för medierna att uppmärksamma datorkulturen? Antagligen beror det till största delen på föryngringen av journalisterna. Många journalister är ju i 30-års åldern och har växt upp med detta. En annan orsak är självklart att datorkulturen är ännu större idag.

Det som är tråkigt (och lite skrämmande) är att det alltså krävs att ungdomarna växer upp och blir journalister innan resten av befolkningen kan upplysas om ungdomskulturen i fråga. Man undrar om distribution av film och musik, samt Internetlagar, hade kunnat sett annorlunda ut om ämnet hade debatterats redan för 15 år sedan.