Thursday, November 17, 2011

using namespace

Есть хорошая книга котрую я давно читал – «Стандарты программирования на C++ 101 правило и рекомендация» Герб Саттер, Андрей Александреску. Там была вот такая рекомендация №59: «Не используйте using для пространств имен в заголовочных файлах или перед директивой #include». И вот я заметил, что в коде сгенеренным мастером ATL используется using namespace в *.h:

...
using namespace ATL;

// CNoNamePreview
class ATL_NO_VTABLE CNoNamePreview :
    public CComObjectRootEx< CComSingleThreadModel >,
    public CComCoClass< CNoNamePreview, &CLSID_NoNamePreview >,
    public IObjectWithSiteImpl< CNoNamePreview >,
...

Вроде ничего такого, т.к. этот хедер дальше нигде не должен включатся. Но как-то непривычно что-ли. То что выше относится к MSVS2010sp1, а вот так делал мастер в MSVS2008sp1:

// stdafx.h
...
#define _ATL_NO_AUTOMATIC_NAMESPACE
...
using namespace ATL;
...

Wednesday, November 16, 2011

Custom Deleter - shared_ptr<> vs unique_ptr<>

Раньше можно было делать так:

// shared_ptr< > way
LPWSTR lpName = NULL;
std::tr1::shared_ptr< WCHAR > spName( (
    spItem->GetDisplayName( SIGDN_FILESYSPATH, &lpName ), lpName ),
        CoTaskMemFree );

Теперь можно и вот так:

// unique_ptr< > way
typedef void ( __cdecl* _COM_DELETER )( LPVOID );

LPWSTR lpName = NULL;
std::unique_ptr< WCHAR, _COM_DELETER > spName( (
    spItem->GetDisplayName( SIGDN_FILESYSPATH, &lpName ), lpName ),
        CoTaskMemFree );

В случае с unique_ptr< > нет дополнительного оверхеда на счетчик и проверки-вызова делитера в рантайме, но теперь приходится указывать тип. А ещё можно делать такое:

std::unique_ptr< int[ ] > ar( new int[ 100 ] );

Thursday, May 5, 2011

Virtual inheritance in C++

Часто встречал вопрос на эту тему. Ответ что это и как работает, как бы намекает, что человек как минимум внимательно читает книги (т.к. большинство людей этот вид наследования не используют). Обычно ответ такой: нужно чтоб избежать двух копий базового класса при «ромбовидном» наследовании.
Обычно на этом всё заканчивается – мол, молодец (читаешь внимательно).
Но вопросы нужно продолжить. Например:
– Что если не использовать виртуальное наследование в данном случае? Как тогда?
О:…
– Во всех ли случаях этот вид наследования нужен, если нет, то в каких?
О: Если нет дублирования данных, только функции – то в принципе это дублирование никому не мешает.
– В MS COM есть базовый класс IUnknown, и есть дальше почти всегда «ромбовидное» наследование, но виртуальность тут не используется, почему?
О: Бинарный стандарт COM требует такую структуру в памяти, где у каждого есть своя копия таблицы IUnknown.
– Как выглядит это в памяти, и что со скоростью?
О: ESC_Boston_01_304_paper, страница 19.

Далее после этих вопросов, можно спросить, а зачем вообще нужно наследование? Что делать если его нет? Хорошо или плохо его использовать, почему, когда, где? Очень болезненная тема для многих. Часто в проекте получается, типа один говорит «Не трогайте мои классы/мою архитектуру!», а ему в ответ «OOP must Go!». Вот в тему очень полезная статья/блог: avoiding inheritance dependency

Friday, April 1, 2011

bits count

Как-то давненько хотел написать пост на эту тему, и вот сегодня увидел мега-баннер от Global Logic:



Собственно теперь повод есть( сори за баян если что ).

ИМХО задача бесполезная, но при этом популярная в собеседованиях С/С++. Решается она не сложно - через деление на 2( я решил не рассматривать этот случай ), либо сдвигом, либо масками. Сдвиг – очевидно более простое решение, и его обычно подразумевают. Вот пример реализации сдвигом:

// Решение сдвигом
int runtime_bits_count( unsigned int n )
{
    unsigned int c = 0;
    for ( ; n; n >>= 1 ) n & 1 && ++c;

    return c;
}

Также нужно заметить, что математики уже давно всё посчитали, и для каждого типа целого есть некий набор хитрых манипуляций и масок. Вот пример для 32-битного целого:

// Более быстрый алгоритм с масками
//     Генри Уоррен мл. Алгоритмические трюки для програмистов.
//     Глава 5 Подсчет битов, листинг 5.1
unsigned int runtime_fast_bits_count( unsigned int n )
{
    n = n - ( ( n >> 1 ) & 0x55555555 );
    n = ( n & 0x33333333 ) + ( ( n >> 2 ) & 0x33333333 );
    n = ( n + ( n >> 4 ) ) & 0x0F0F0F0F;
    n = n + ( n >> 8 );
    n = n + ( n >> 16 );

    return n & 0x0000003F;
}

Я не зря написал в названии функций ‘runtime’, ведь функция считает установленные биты только во время выполнения. Но если входящий параметр константа/дефайн/число – то можно было б посчитать всё во время компиляции. Вот пример решения для однобайтного целого на Си:

// Compile-time bits_count в стиле С
#define bits_count( n ) \
    ( n & 0x01 ? 1 : 0 ) + ( n & 0x02 ? 1 : 0 ) + \
    ( n & 0x04 ? 1 : 0 ) + ( n & 0x08 ? 1 : 0 ) + \
    ( n & 0x10 ? 1 : 0 ) + ( n & 0x20 ? 1 : 0 ) + \
    ( n & 0x40 ? 1 : 0 ) + ( n & 0x80 ? 1 : 0 )

const unsigned char b = 120;
int res = bits_count( b ); // compile-time

Для однобайтного на С++:

// Тот же подход но в стиле С++
template < unsigned char n >
struct bits_count
{
    enum
    {
        RES = ( n & 0x01 ? 1 : 0 ) + ( n & 0x02 ? 1 : 0 ) +
              ( n & 0x04 ? 1 : 0 ) + ( n & 0x08 ? 1 : 0 ) +
              ( n & 0x10 ? 1 : 0 ) + ( n & 0x20 ? 1 : 0 ) +
              ( n & 0x40 ? 1 : 0 ) + ( n & 0x80 ? 1 : 0 )
    };
};

const unsigned char b = 120;
int res = bits_count < b >::RES; // compile-time

Круто. А теперь еще немножко и получим менее зависимую от ширины типа реализацию:

template < unsigned int x >
struct bitscount
{
    static const unsigned int n = bitscount < x / 2 >::n + x % 2;
};

template < >
struct bitscount < 0 >
{
    static const unsigned int n = 0;
};

#define dec2bin( x ) bitscount < ( x ) >::n

int _tmain( int argc, _TCHAR* argv[] )
{
    const int b = 60000;     // 32 bit!
    size_t t = dec2bin( b ); // in compile-time

    return 0;
}

Теперь остаеться написать реализацию для n-байтного целого, где если это константа – вычисление должно быть во время компиляции. Иногда говорят: «надо заставить компилятор за нас думать», оно вроде сейчас так и получается, но думаю всё равно я, а он компилит). Думал, думал – и не придумал). Зато нашел полезную вещь которая детектит константность во время компиляции:

struct check_const
{
    struct Temp { Temp( int x ) { } };
    static char chk2( void* ) { return 0; }
    static int  chk2( Temp  ) { return 0; }
};
#define is_const( t ) \
    ( sizeof( check_const::chk2( 0 + !!( t ) ) ) != sizeof( check_const::chk2( 0 + !( t ) ) ) )

Monday, March 28, 2011

VA или ...

Была когда-то у меня задача связанная с VA. Был некий макрос:

#define del( pBuf ) del_impl( ( pBuf ), __FILE____LINE____FUNCTION__ )

детали del_impl не важны, но внутри в конце концов вызывался оператор delete. Потом я захотел модифицировать макрос, чтобы если передается опционально второй параметр – то внутри вызывался бы delete[]. С макросами это не так просто было сделать сразу. Порисёчив в инете я тогда придумал некое решение, но мне кажется что есть лучше. Вот то что тогда вышло (пример проверил в студии):

#include < stdarg.h >
#include < iostream >

#define SENTINEL -32768
template < class T >
inline void del_impl(
    T* &pBuf,
    const char*  szFile,
    unsigned int nLine,
    const char*  szFunc,
    unsigned int nCount,
    ...
    )
{
    va_list args;
    va_start(argsnCount);

    // bla bla bla...

    SENTINEL == va_arg( argsint ) ? delete pBuf : delete[] pBuf;

    pBuf = NULL;
    va_end(args);
}

#ifdef _MSC_VER
#   define del( pBuf, ... ) \
        del_impl( ( pBuf ), __FILE____LINE____FUNCTION__, \
            1, __VA_ARGS__SENTINEL )
#else
#   define del( pBuf, ... ) \
        del_impl( ( pBuf), __FILE__, __LINE__, __FUNCTION__, \
            1, ##__VA_ARGS__, SENTINEL )
#endif // _MSC_VER

class CTest
{
public:
    ~CTest( )
    {
        static int i = 0;
        std::cout << __FUNCTION__ << ++i << std::endl;
    }
};

int _tmain( int argc_TCHARargv[] )
{
    CTestp  new CTest;
    del( p );

    CTestpp = new CTest[10];
    del( pptrue );
    // del( pp ) // WRONG!
    //    in this case, in debug we will have an assert:
    //   _ASSERTE(_BLOCK_TYPE_IS_VALID(pHead->nBlockUse)); from 'dbgdel.cpp'

    return 0;
}

В общем если есть более красивое или правильнее или вообще другое решение – было бы прикольно его увидеть.

Tuesday, March 22, 2011

Задача по С++

Хорошая задача для собеседования "опытных" ребят по С++.
Допустим, у вас есть некий сторонний код интерфейсов/каллбеков, и вам нужно отнаследоваться от всех (в данном случает от двух):

// -- third part code (you can't change this)
Struct IFoo { virtual void Test() = 0; }; // you can't change this
Struct IBla { virtual void Test() = 0; }; // you can't change this
// -- end of third part code

далее нужно реализовать функцию Test для каждого интерфейса отдельно:

// your code (but it does not work rightly)
Struct CTest : public IFoo, public IBla
{
    Virtual void Test()
    {
        std::cout << "I'm a ...?" << std::endl;
    }
};

далее опять сторонний код, к примеру такой:

// -- third part code (you can't change this)
Int _tmain(int argc, _TCHAR* argv[])
{
    CTest test;

    IFoo& foo = test;
    foo.Test(); // must be: "I'm a foo" in out

    IBla& bla = test;
    bla.Test(); // must be: "I'm a bla" in out

    return 0;
}
// -- end of third part code

В общем я надеюсь задача ясна - нужно придумать как разрулить ситуацию не меняя интерфейсы и тестовый код.
Решение задачи ниже(белым по белому).

// code code code
#include < iostream >

struct IFoo { virtual void Test() = 0; };
struct IBla { virtual void Test() = 0; };

struct IFooImpl : IFoo
{
    virtual void Test() { return FooTest(); }
    virtual void FooTest() = 0;
};

struct IBlaImpl : IBla
{
    virtual void Test() { return BlaTest(); }
    virtual void BlaTest() = 0;
};

struct CTest : public IFooImpl , public IBlaImpl
{
    virtual void BlaTest() { std::cout << "I'm a bla" << std::endl; }
    virtual void FooTest() { std::cout << "I'm a foo" << std::endl; }
};

int _tmain(int argc, _TCHAR* argv[])
{
    CTest test;

    IFoo& foo = test;
    foo.Test();

    IBla& bla = test;
    bla.Test();

    return 0;
}
// code code code