2024-04-22

std::vector<bool> perormance penalty

I have created a test for various vector<T> types (T in int, bool, char, int_fast8_t, uint_fast8_t).

Summary

I have found that a vector of int is 4 times faster than bool, but char (and *_fast8_t types) can be even faster: 20 times when writing and 50 times when reading

Vector size is 400 million (8 long pattern used 50 million times). First we populate a vector (write), then ve copy the vector to another (read+write). We approximate the read by subtracting the two (read = read+write - write).

type write [ms] read+write [ms] read [ms]
int9212937
bool419892473
char23329
int_fast8_t23329
uint_fast8_t23318

  • CPU: AMD EPYC 7B12
  • OS: Linux version 6.6.15 amd64
  • Compiler: gcc version 13.2.0
  • Compliation: g++ -std=c++17 -O3 -W -Wall -Wextra -pedantic -flto vector_bool.cpp -o vector_bool
  • Source: pastebin.com

Background

In the standard library of C++ vector<bool> is specialized for space. This is a very old legacy from a time where generally speaking RAMs were very small. In this container the bool type ‐ which uses 1 or even more bytes to store a true/false ‐ is not used for storage: rather a larger integer type is used as underlying array element to store multiple bits. In this case writing a bit requires reading the corresponding integer, altering it with bitwise operations, then writing it back to the container.

Search the Internet if you would like to find other disadvantages of this specialization. This post is only about the magnitude of the performance penalty this decision have caused.

Recommended workarounds

Some alternatives

  • Use *_fast8_t as T.
  • Use boost::container::vector<bool> instead.
  • Create a class Bool that stores a bool and use it as T. This seems to be much more like a school/learning project rather than a production solution. But who knows: there might be circumstances where this is the way.

2019-01-21

Kormos Anett írása Andy Vajna halálának online fogadtatásáról

Idézet:

Egy barátom azt tanácsolta, ne írjak erről a témáról, mert túlságosan vékony jég. Nekem pedig az jutott eszembe magamba fojtott riposztként, hogy ha vékony jég azzal kapcsolatban véleményt nyilvánítani: hogyan kéne reagálnunk arra, ha valaki meghal, akkor üsse bassza, szakadjon be alattam, mert ez a jég számomra nem megfelelő élőhely.

Ma reggel meghalt Andy Vajna.

Az internetet pedig rögtön elárasztották a haláláról, és a felesége anyagi fellendüléséről szóló mémek, amelyek belőlem valahogy nem tudták előcsalni az önfeledt kacagás ingerét.

Nem Andy Vajna vagy az özvegye miatt, nem a gyász vagy a mélységes együttérzés miatt.

Hanem azért, mert a derű helyett riadalom költözött belém. Riaszt, hogy mennyire elfogadottá, sőt lájkolandóvá vált az, hogy sokan egy jó poén kedvéért könnyedén levetik magukról emberarcukat …márha egyáltalán szokták még hordani.

Ti is tudjátok, messze vagyok a tapintatoskodástól, nem jellemző, hogy eltartott kisujjal fogom meg a poént. Azt vallom, bármiből lehet viccet csinálni: buzikból, zsidókból, cigányokból, nőkből, férfiakból, anyósokból, apósokból, gyerekekből és… igen, akár a halálból is. Feltéve, hogy képesek vagyunk olyan szellemesen kiforgatni azt a sarkából, hogy felülírjuk magának a HALÁL tényének a drámáját. Ha ezt a lécet nem sikerül megugrani, kár is neki futni, biztos bukás a vége.

Ugyanakkor azt is gondolom, hogy nyitott koporsó felett szotyihéjat köpködve az elhúnyton élcelődni, minimum nem elegáns, de inkább állati nagy parasztság. A szellemeskedésre meg kell várni a megfelelő időt, alkalmat, formát, helyet és közönséget. Ez most nem sikerült.

Itt azonban még kerestem a mentségeket: hogy a virtuális világ már teljesen leradírozta a valóság kontúrját, a mémgyárosok pedig nyilván röpke poéngyártási kényszerük hatása alá kerülve felejtették el, hogy meghalt valaki. De hát tudjuk: a virtuális világ vérszomjas csecsemő, a bölcsőjét pedig mi magunk ringatjuk.

Ezzel a keserű summázattal túl is libbentem volna a témán, amikor kiderült, hogy nagyobb a baj. Andy Vajna halálának napján rengetegen érezték úgy, hogy itt az idő, hogy indulataik és gyűlöletük rövidláncon tartott vérebeit végre ráuszítsák Vajna Tímeára.

És ez sokkolt igazán.

Mert ennek a virtuális vandalizmusnak én nem akarom kutatni az okait, és nem akarom felmenteni az elkövetőit.

Mert ettől valóban belém költözött a szomorúság és a részvét.

A gyász.

Csak az én gyászom nem Andy Vajnának szól, hanem azoknak, akikből az emberség úgy halt ki, hogy nekik talán fel sem tűnt.

Hogy miért osztom meg más posztját, mint másolatot?

Magyar Hang: A gyűlölködés ellen szólalt fel Kormos Anett, a Facebook törölte a posztját

2015-07-24

undefined reference to `vtable for C'

This post intends to be a quick help for anyone who struggles with this error. This error came to me when I tried to compile a larger source code which was already compiling on Windows. It took me more than an hour to figure it out. Here is what I've found, from solution to details.

Possible reasons

  • C.cpp was not compiled.
  • C.o was not added to inputs.
  • There is a virtual method in the C class that is not defined (body of method not present).

Minimalistic reproduction

vtrep.cpp

class C
{
public:
  virtual ~C()
  { }
public:
  virtual int id();
};

int main()
{
  C c;
  return 0;
}

Compilation

$ g++ -c vtrep.cpp -o vtrep.o
$ g++ -o vtrep vtrep.o
vtrep.o: In function `C::~C()':
vtrep.cpp:(.text._ZN1CD2Ev[_ZN1CD5Ev]+0x13): undefined reference to `vtable for C'
vtrep.o: In function `C::C()':
vtrep.cpp:(.text._ZN1CC2Ev[_ZN1CC5Ev]+0xf): undefined reference to `vtable for C'
collect2: error: ld returned 1 exit status

Note that the error messages tell nothing about the id() method.

2015-04-10

Java ArrayList resize

Once I had an array:

    static final Apple[] apples = new Apple[4];

Then circumstances changed so, Apple became generic, so my code changed to

    static final Apple<T>[] apples = new Apple<T>[4];

which produced compile errors as Cannot create a generic array of Apple<T>, so I was forced to switch to ArrayList like:

    static final ArrayList<Apple<T>> apples = new ArrayList<>(4);
    // 4 means capacity here not size.

I have some code in a static initializer block that fills apples using calculated indexes, so it is not a sequence of add(e) calls and it is not desirable for me to rearrange it that way.

I would like to resize the array to have 4 elements, all of them null. A manual way to achieve it is this:

    static {
      for(int i=0; i<4; ++i)
        apples.add(null);
      // ORIGINAL index-magic based code here.
    }

Which is super effective but not so elegant.

Unfortunately all my search efforts on the Internet were derailed because of posts that suggest ensuring capacity and then adding elements with add(e). None of them addressed the original question of resizing an ArrayList using a filler element. The OPs of those questions were satisfied with those answers, but I really would like to have the answer to the original question.

Until then this will go:

  /**
   * Resizes and returns an {@code ArrayList}.
   * Capacity is not freed upon shrinking. At most one allocation happens.
   * 
   * @param list
   *          Input to be altered, return value.
   * @param size
   *          Desired size
   * @param filler
   *          Additional elements - if any - will be set to this.
   * @return Altered list.
   */
  public static <T> ArrayList<T> resize( ArrayList<T> list, int size, T filler ) {
    for( int i = list.size(); i > size; --i )
      list.remove( i - 1 );
    list.ensureCapacity( size );
    for( int i = list.size(); i < size; ++i )
      list.add( filler );
    return list;
  }

  /**
   * Resizes and returns an {@code ArrayList}.
   * Capacity is not freed upon shrinking. At most one allocation happens.
   * Additional elements - if any - will be set to null.
   * 
   * @param list
   *          Input to be altered, return value.
   * @param size
   *          Desired size
   * @return Altered list.
   */
  public static <T> ArrayList<T> resize( ArrayList<T> list, int size ) {
    return resize( list, size, null );
  }

It is slower than the original for based ad-hoc solution, but it is quicker to read at the place of usage, and resizing of arrays do not (should not) happen that often, as I would seriously doubt the general quality of a code where an array resizing takes place at a hot-spot.

2013-02-09

Senior developer / Tapasztalt fejlesztő

Senior Developer explaining how to use his library

A tapasztalt fejlesztő elmeséli, hogy hogyan kell a programját használni

@ DevOps reactions

2012-11-16

Prizma napló #2

A két szemem fókuszpontjainak távolsága kb ötöde szemüvegben, mint szemüveg nélkül. A mérési nehézségekhez képest egész jóra sikerültek a lencsék.

A kilincset egyszer-egyszer még mindig eltévesztem, de már csak pár centivel.

Az autók mozgásával kapcsolatban nincsen a korábban tapasztalt bizonytalanság (amit rögtön nem is írtam le).

A szivárvány effektet már csak nagyon ritka fényviszonyok mellett látom. Cserébe minden egy picit olyan, mintha finoman meg lenne rajzolva a kontúrja.

Eddig még egyszer sem sikerült elfelejtenem, hogy rajtam van, azaz még nem szoktam meg.

2012-11-09

Prizma napló #1

Minden kontúr szivárványos. A szemész hölgy ezen nem lepődött meg. Azt mondta, hogy meg fogom tanulni a szivárvány nem látását.

Vélt vagy valós térélmény javulás

Emlékezetből pontosan fogom a kilincset, látás alapján 10 centit tévedtem elsőre.

Bizonytalan voltam elsőre egy-két tárgy darabszámát illetően.

A szemüveg felvétele és levétele után nem tapasztalom a megjósolt lehetséges tüneteket (több perces átszokási idő, kettős látás, bizonytalan távolság érzékelés)

Szemüveg nélkül a bal szemem a domináns, szemüvegben a jobb.

Prizma napló #0

Körülbelül egy hónapja volt a melóhelyemen egy szemészeti szűrés, amelyen kiderült (csodák csodája), hogy szemtengely ferdülésem van. Nem volt meg a helyszínen minden műszerük ami a paraméterek pontos kiméréséhez szükségesek lettek volna, ezért elhívtak a székesfehérvári bázisukra további vizsgálatokra, amelyre el is mentem.

A színpad úgy néz ki, hogy egy szemésztáblára felfestenek egy szálkeresztet úgy, hogy a függőleges és a vízszintes szár más-más polarizációval látszott, nekem pedig egy olyan szemüveget (szemészeti lencseállványt) adtak amely elé polár-szűrőket helyeztek. Így a bal szememmel csak a függőleges szárat láttam a jobb szememmel csak a vízszinteset.

Ilyenkor jött a belépő mutatvány. Képes vagyok-e az agyammal mindkét szem képét egyszerre látni. Némi játék után a mutatvány sikerült. A körülbelül 5 méterre lévő falon egymástól fél méter távolságra megláttam az ábra két komponensét. Függőleges irányban is volt némi elcsúszás, az eltérés nagyja vízszintesen volt.

Ekkor következett a prizma típusú lencsék cserélgetése, amelyek a szemeimből kiinduló nézést megpróbálták párhuzamosra téríteni (értjük). A függőleges irányú igazítás tökéletessé vált, a vízszintes nem nagyon akart összejönni. Az agyam elutasította azt az ötletet, hogy a bal és a jobb szemem által látott tárgy megegyezik. Amikor a két komponensnek elvileg egymáson kellett volna lennie, akkor a vízszintes komponens el kezdett gyors tempóban ugrálni a függőleges komponens két oldala között.

Az így kimért közelítő értékeket elfogadtuk és felvéstük az adatlapra. Ez alapján elkészíthették számomra azt a szemüveget, amihez ha jól hozzászokom, akkor újabb mérések következhetnek. Ha az agyam tényleg megtanulja a normál emberek látását és sikerül pontosan kimérni a szemtengely ferdülést, akkor az egy műtéttel drasztikusan csökkenthető. Szerencsés esetben meg is szűnhet.

A szemüveget ma vettem át és el is kezdtem hordani. A viccesebb vizuális élményeimet pedig meg is fogom osztani itt a blogon. Meg amúgy is, diagnosztikai szempontból esetleg hasznos lehet, ha naplózva vannak.

2012-04-27

Easy but not too slow integer to string conversion

In my previous post I compared the speed of different methods of converting integer to string. The problem was that these solutions both relied on the environment somehow and cannot be used on an adhoc manner like messages[i] = std::string("Error found at line #") + IntToString(lineNumbers[i]) + ".";. These both need some circumstancial preparation like creating a buffer (char buffer[...]) or a std::stringstream outside the loop and resetting it after usage. So I started on a journey to create a kitchen-ready preparation-less yet not too slow implementation for my imaginary IntToString() function.

#include <iostream>
#include <limits>
#include <sstream>
#define __STDC_FORMAT_MACROS
#include <inttypes.h>

// Utility functions and structs

template<typename Unsigned>
char * DigitsToString(Unsigned i, char * endPtr)
{
    char *&cursor(endPtr);
    *cursor = 0;
    if(!i)
    {
        *--cursor = '0';
        return cursor;
    }
    for(;i;i/=10)
    {
        *--cursor = char(i%10+'0');
    }
    return cursor;
}

template<typename Unsigned>
std::string UnsignedToString(Unsigned i)
{
    const size_t bufsize = std::numeric_limits<Unsigned>::digits10 + 1;
    char buffer[bufsize];
    return DigitsToString(i,buffer+bufsize-1);
}

template<typename Signed>
std::string SignedToString(Signed i)
{
    if(i>=0)
    {
        return UnsignedToString(i);
    }
    const size_t bufsize = std::numeric_limits<Signed>::digits10 + 2;
    char buffer[bufsize];
    char *cursor = DigitsToString(-i,buffer+bufsize-1);
    *--cursor = '-';
    return cursor;
}

template<bool is_signed,typename Integral>
struct ToStringConverter { };

template<typename Integral>
struct ToStringConverter<false,Integral>
{
    static std::string Do(Integral i){ return UnsignedToString(i); }
};

template<typename Integral>
struct ToStringConverter<true,Integral>
{
    static std::string Do(Integral i){ return SignedToString(i); }
};

// The IntToString()

template<typename Integral>
std::string IntToString(Integral i)
{
    return ToStringConverter<std::numeric_limits<Integral>::is_signed,Integral>::Do(i);
}

// Testing my IntToString()

template<typename Integral, Integral count>
void testFun()
{
    for(Integral i=0; i<count; ++i)
    {
        IntToString(i);
    }
}

// An std::stringstream based integer to string conversion

template<typename T>
std::string StreamedToString(const T& i)
{
    std::stringstream ss;
    ss << i;
    return ss.str();
}

// Testing of the std::stringstream based integer to string conversion

template<typename Integral, Integral count>
void testStream()
{
    for(Integral i=0; i<count; ++i)
    {
        StreamedToString(i);
    }
}

#ifndef TEST_CASE
#define TEST_CASE 1
#endif

int main()
{
#if TEST_CASE == 1
    testFun<uint64_t,100*1000*1000>();
#endif
#if TEST_CASE == 2
    testStream<uint64_t,100*1000*1000>();
#endif
    return 0;
}

This is far from being a market-ready product, but it might be useful. Speed test:

$ g++ -W -Wall -Wextra -pedantic -O2 int_to_str_speed2.cpp -DTEST_CASE=1 -o int_to_str_speed2
$ time ./int_to_str_speed2

real    0m12.404s
user    0m12.397s
sys     0m0.008s
$ g++ -W -Wall -Wextra -pedantic -O2 int_to_str_speed2.cpp -DTEST_CASE=2 -o int_to_str_speed2
$ time ./int_to_str_speed2

real    1m36.204s
user    1m36.174s
sys     0m0.008s

Speedy integer to string conversion in C++

Test

This is a log entry about playing around the question. There are robust ways of converting integers to string already implemented out there, for example FastFormat. Good for them. Let's see how I played on my playground.

I measured the speed of sprintf(), snprintf() and my own implementation. I tried to make most of the decisions compile-time. I didn't care to write time measurement into my program, instead I'm using GNU's time utility with three different versions of my program separated using macros.

#include <iostream>
#include <cstdio>
#include <limits>
#define __STDC_FORMAT_MACROS
#include <inttypes.h>

#ifndef TEST_CASE
#define TEST_CASE 1
#endif
#ifndef TESTCOUNT
#define TESTCOUNT 100*1000*1000
#endif
#ifndef INTTYPE
#define INTTYPE uint64_t
#define INTFMT PRIu64
#endif

template<typename Integral>
char *myUIntToStr(Integral i, char *endPtr)
{
    char *&cursor(endPtr);
    if(!i)
    {
        *--cursor='0';
        return cursor;
    }
    for(;i;i/=10)
    {
        *--cursor = char(i%10+'0');
    }
    return cursor;
}

int main()
{
    const size_t BUFSIZE = std::numeric_limits<INTTYPE>::digits10 + 2;
    char buffer[BUFSIZE];
    buffer[BUFSIZE-1] = 0;
#if TEST_CASE == 1
    for(INTTYPE i = 0; i<TESTCOUNT; ++i)
    {
        sprintf(buffer,"%" PRIu64,i);
    }
#endif
#if TEST_CASE == 2
    for(INTTYPE i = 0; i<TESTCOUNT; ++i)
    {
        snprintf(buffer,BUFSIZE,"%" PRIu64,i);
    }
#endif
#if TEST_CASE == 3
    char * const endPtr = buffer + BUFSIZE - 1;
    char * output;
    for(INTTYPE i = 0; i<TESTCOUNT; ++i)
    {
        output = myUIntToStr(i,endPtr);
    }
#endif
#if TEST_CASE == 4
    std::stringstream ss;
    for(INTTYPE i = 0; i<TESTCOUNT; ++i)
    {
        ss << i;
        ss.str(""); // clearing
    }
#endif
    return 0;
}

In many cases the arithmetic correctness of a program or interoperability of a binary format can be achieved by using fixed size integers like uint64_t instead of the fluffy definition of short, int, long, etc.. These fixed size integers are defined in the inttypes.h header file of the C99 standard. I don't know any standard options for this purpose under C++03, so I chose to use this second best solution. I'm using this decision for a while and the time is finally on my side: The C++11 standard includes this header. The defining of __STDC_FORMAT_MACROS before including this header results in the creation of printf() formatters for those types (without the % sign).

In theory this is a portable source code in practice (sic), in practice I haven't tried. This thing is only a proof of a concept.

The original idea for the conversion implementation came from abelenky at a stackoverflow.com question which was about the alternatives to itoa(). I added that the buffer is allocated outside the function on stack (something like for free) and a pointer to its end passed into the function.

Environment

  • OS: Debian (6.0.4 Squeezee) GNU/Linux (2.6.32-5-amd64)
  • G++ 4.4.5
  • CPU: Intel(R) Core(TM)2 CPU 6400 @ 2.13GHz

Results

$ g++ -W -Wall -pedantic -Wextra -O2 -DTEST_CASE=1 sprintf_speed.cpp -o sprintf_speed
$ time ./sprintf_speed

real    0m16.776s
user    0m16.757s
sys     0m0.012s
$ g++ -W -Wall -pedantic -Wextra -O2 -DTEST_CASE=2 sprintf_speed.cpp -o sprintf_speed
$ time ./sprintf_speed

real    0m16.951s
user    0m16.953s
sys     0m0.000s
$ g++ -W -Wall -pedantic -Wextra -O2 -DTEST_CASE=3 sprintf_speed.cpp -o sprintf_speed
$ time ./sprintf_speed

real    0m4.295s
user    0m4.296s
sys     0m0.000s
$ g++ -W -Wall -pedantic -Wextra -O2 -DTEST_CASE=4 sprintf_speed.cpp -o sprintf_speed
$ time ./sprintf_speed

real    0m15.443s
user    0m15.429s
sys     0m0.012s

The difference between the sprintf() and snprintf() version was around 2 to 4 percent consistently in favor of sprintf(). It's unfair to compare these guys to my solution because they also have to parse their given formatter parameter. I believe that my solution is somewhat optimal. The fourth case with std::stringstream is there for reference, that is the clean C++ style portable way for doing the conversion. Surprisingly fast.

2012-03-14

Invoking template constructor of a template class

#include <iostream>
#include <deque>
#include <list>

template<typename T>
class Holder
{
public:
    Holder(){}

    template<typename I>
    Holder(I begin, I end)
      : m_data(begin,end)
    { }
private:
    typedef std::deque<T> Data;
    Data m_data;
};

int main()
{
    std::list<int> ints;
    ints.push_back(3);
    ints.push_back(4);
    ints.push_back(5);

    // Works implicitly:
    Holder<int> HoldedInts(ints.begin(), ints.end());
    Holder<int> *pHI_1 = new Holder<int>(ints.begin(), ints.end());
    delete pHI_1;

    // Does not work explicitly:
    // Holder<int> HoldedInts<std::list<int>::const_iterator>(ints.begin(), ints.end());
    // Holder<int> *pHI_1 = new Holder<int><std::list<int>::const_iterator>(ints.begin(), ints.end());
    // delete pHI_1;

    return 0;
}

2012-03-02

shared_ptr vs inheritance

A picture is worth a thousand words

#include <iostream>
#include <boost/smart_ptr/shared_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>

class A: public boost::enable_shared_from_this<A>
{
public:
    typedef A Self;
    typedef Self Root;
    typedef boost::shared_ptr<Self> SPtr;

public:
    A(){ std::cerr << "DEBUG: A::A()" << std::endl; }
    virtual ~A(){ std::cerr << "DEBUG: A::~A()" << std::endl; }
    SPtr getSPtr()
    {
        return shared_from_this();
    }
};

class B: public A
{
public:
    typedef B Self;
    typedef A Base;
    typedef Base::Root Root;
    typedef boost::shared_ptr<Self> SPtr;

public:
    B(){ std::cerr << "DEBUG: B::B()" << std::endl; }
    virtual ~B(){ std::cerr << "DEBUG: B::~B()" << std::endl; }
    SPtr getSPtr()
    {
        return boost::dynamic_pointer_cast<Self>(Root::getSPtr());
    }
};

#define CHECKPOINT std::cerr << "CHECKPOINT: " __FILE__ " (" << __LINE__ << ")" << std::endl

int main()
{
    CHECKPOINT;
    A::SPtr spX(new A);
    CHECKPOINT;
    B::SPtr spY(new B);
    CHECKPOINT;

    return 0;
}

Compile!

g++ -W -Wall -Wextra -pedantic -Os shared_vs_inheritance.cpp -o shared_vs_inheritance

Run!

CHECKPOINT: shared_vs_inheritance.cpp (42)
DEBUG: A::A()
CHECKPOINT: shared_vs_inheritance.cpp (44)
DEBUG: A::A()
DEBUG: B::B()
CHECKPOINT: shared_vs_inheritance.cpp (46)
DEBUG: B::~B()
DEBUG: A::~A()
DEBUG: A::~A()

2011-12-20

bash: adding or subtracting days from a date

I had this problem at my workplace. As I crawled through the web I saw that this is a very common question and rookies are directed to a forum at www.unix.com where someone reimplemented the World (many calendar functions) in bash. His solution is a very long and complicated lib which is an overkill for this kind of tiny task we have at hand.

My solution

t_days=30            # I needed stuff from a this long period,
t_reftime=2011-11-14 # just before this date

# 1st: Translate the reference time to UNIX timestamp:
ts=`date --utc -d "$t_reftime" +%s`
# 2nd: Convert 30 days to seconds, subtract it from the above
# and add it to the UNIX epoch. Format as you like. I liked %Y-%m-%d
sdate=`date --utc -d "1970-01-01 $((ts-t_days*86400)) sec" +%Y-%m-%d`
# $sdate is now "2011-10-15"

2011-11-14

C++: Return type of assignment operator

I had a confusion about whether to return with constant reference or non-constant reference from operator=. It seemed clever and practical to return T const&, because of the following faulty reasoning:

T a,b;
a = b = T(100,200,"something");

The fully bracketed form of the second line is:

a = (b = T(100,200,"something"));

So if we return T const& from T::operator=(T const& rhs) it is sufficient for that purpose. And also when we accidentally write something silly like (a = b) = T(100,200,"something") it will help us by giving a compile error.

Reasoning for non-constant return

The nice argument is that there are useful cases when we do want to modify the result of an assignment, for example:

T a,b,c;
// ...
std::swap(a=b,c); // #1
(b=c).Transponate(); // #2
// ...

The strong argument is that many STL containers require their type parameter(s) to fulfill the Assignable[1] concept, which defines assignment operator to return non-constant reference.

2011-09-23

Ruby, oh my increment...

Quote from ruby-doc.org:

9. Ruby has no pre/post increment/decrement operator. For instance, x++ or x-- will fail to parse. More importantly, ++x or --x will do nothing! In fact, they behave as multiple unary prefix operators: -x == ---x == -----x == ...... To increment a number, simply write x += 1. (An explanation for this language design by the author of Ruby can be found at http://www.ruby-talk.org/2710.)

And you are eager to know an explanation for that decision, and you click and you get:

The domain ruby-talk.org may be for sale. Click here for details.

I have been convinced as of now that Ruby is a really good programming language for university studies, but it is yet to become an applicable language in the industry. Will it ever reach that seriousness? It has been infant for a long time and haven't yet reached that level...

2011-08-04

Ruby true or false

The test

def true?(x)
    if x
        return "true"
    else
        return "false"
    end
end

tests = [false,true,0,1,nil,[],{},"","0","1","true","false"]

tests.each do |test|
    puts test.inspect+":\t "+true?(test)+" ("+test.class.to_s+")"
end

The results

false:   false (FalseClass)
true:    true (TrueClass)
0:       true (Fixnum)
1:       true (Fixnum)
nil:     false (NilClass)
[]:      true (Array)
{}:      true (Hash)
"":      true (String)
"0":     true (String)
"1":     true (String)
"true":  true (String)
"false":         true (String)

For later reference.

2011-08-02

Álmaid nője 1 perce halott / The woman of your dreams died a minute ago

English version follows


A mindenki.hu egy olyan szájt, ahol az emberek feltehetnek és megválaszolhatnak személyes preferenciákra vonatkozó kérdéseket, majd aztán megkereshetik hogy ki mennyire hasonlít hozzájuk vagy különbözik tőlük.

Volt ott egy kérdés: Lefeküdnél álmaid nőjével, úgy hogy már 1 perce halott?

  • A: persze, ha adódik egy alkalom, akkor nem szabad kihagyni.
  • B: ez undorító!!
  • C: lány vagyok
  • D: attól függ, hogyan halt meg

Az eredmények:

  • A: 2%
  • B: 52%
  • C: 33%
  • D: 11%

Ha ezek küzül az eredmények közül megnézzük a férfiakra vonatkozó részt:

  • A: 3%
  • D: 17%

Azaz a férfiak 20%-a [te mondod meg].


English version

The mindenki.hu is a site where people can raise and answer questions about personal preferences and then they can see who is similar and who is different to them.

There was a question: Would you "sleep" with the woman of your dreams if she were dead for 1 minute?

  • A: Of course. If an opportunity raises, it should not be missed.
  • B: This is disgusting.
  • C: I am a woman.
  • D: It depends on the way she died.

The results:

  • A: 2%
  • B: 52%
  • C: 33%
  • D: 11%

Taking the shocking results only on men:

  • A: 3%
  • D: 17%

So 20% of men are [you name it].

2011-05-18

Programming language inventor OR serial killer?

Kóder vagy sorozatgyilkos?

Can you tell a coder from a cannibal? Try to work out which of the following spent their time hacking computers and which preferred hacking away at corpses instead.

Killer Quiz

2011-05-04

The bad and ugly: Ruby strings

Ruby is a language like Java, everything is reference to object. An assignment does not produce copy of the value, but the copy of the reference. So if you have an a object, and you say b=a then you will have a b object reference pointing to the same place as a.

The naive approach of a string is that if I say a='x12345y'; b=a; b.sub!('234','---'); will result in an a which's value is 'x12345y' and a b which's value is 'x1---5y'.

The Java guys invented the immutable pattern which means that after a string is created it cannot be modified. The sensation of modification comes from the construction of new strings from old ones, like s = s.concat("TEST") where s.concat("TEST") creates a new string which's reference may or may not be stored back at s itself.

But Ruby has weird behavior:

original = '|123|123|123|123|'
s = original
s['123']='TEST'
print(s,"\n")
s = original
s.sub!('123','TEST')
print(s,"\n")
will output
|TEST|123|123|123|
|TEST|TEST|123|123|
You do not know enough - they say, There are immutable objects in ruby too!. I just have to call the freeze method and the object will be immutable. Let's try it.

original = '|123|123|123|123|'
original.freeze # <--- NEW GUY
s = original
s['123']='TEST'
print(s,"\n")
s = original
s.sub!('123','TEST')
print(s,"\n")
will output
stringtest.rb:4:in `[]=': can't modify frozen string (TypeError)
        from stringtest.rb:4

That is just plain wonderful. We are still on the same place: nowhere. What would be the solution? Nothing sane is available. You have to use s = String.new(original) instead of simple assignment. This is a terrible looking pain in the ass solution.

Who the hell knows where was my string declared at the first time? Who knows what will be broken if I change a string I got from somewhere? Who will find the real problem for an error message like File not found: 'TEST'?

2011-05-02

Unexpected behavior: pointer to constant

Preface: some words about correct 'const' syntax

The general rule is that the const keyword affects its immediate left neighbor. Sic! So the correct syntax for defining a pointer to a constant integer is int const * pInteger. The history proved that the traditional style is not sufficient, but is still maintained: If the const keyword is the leftmost in a type specification then it affects its immediate right neighbor.

Constants are to be disposed too

#include <iostream>
class A {
public:
        A(){ std::cout << "Risen #" << (idx=++counter) << std::endl; }
        ~A(){ std::cout << "Fallen #" << idx << std::endl; }
private:
        int idx;
        static int counter;
};
int A::counter = 0;

int main(){
        A const first;
        return 0;
}

will output (as expected)

Risen #1
Fallen #1

As we can observe, the destructor of the const-qualified first variable was called. That extrapolates that the const qualifier does not protect from calling the destructor on a pointer which is a weird looking thing for the first time.

The unexpected behavior

#include <iostream>
class A { /* same as the above */ };
int A::counter = 0;
void f(A const * pTest){ delete pTest; }

int main(){
        A *pFirst = new A, second;
        f(pFirst);
        A const third;
        return 0;
}

will compile flawlessly and will produce

Risen #1
Risen #2
Fallen #1
Risen #3
Fallen #3
Fallen #2

The bad idea

Let's make it impossible to call a delete on pointers to constants! Bad idea. The following example must be possible.

Vector2D const * pVector = new Vector2D(3,2);
// ...
delete pVector; // <--- This is it.

And the deletion of a pointed value must be delegable to functions. That's why the f() was able to delete its parameter.

One may feel unsafe at this moment, but it is not really a problem in the industry: almost nobody has the clue that it is even possible, and nobody wants to do that anyways, except when it is the main goal. But then it's not a trap.