четверг, 31 марта 2011 г.

Вложенные классы в C++

Недавно при работе с boost::asio пришлось заглянуть в один из основных хедеров данной библиотеки, а именно в io_service.hpp. При просмотре объявления класса io_service я обратила внимание на следующие строки в самом начале:

class io_service : private noncopyable
{
private:
// что-то
public:
    class work;
     friend class work;

     class id;

     class service;

     class strand;
    // что-то
};

Я заметила, что класс work является вложенным(nested) и одновременно является другом класса io_service. Вспомнив, что в C++ вложенный класс ничем не отличается от обычного класса, кроме того, что он ограничен областью видимости включающего(enclosing) класса, я решила-таки поставить небольшой эксперимент.

#include
using namespace std;

class A
{
     A(int a, int b) : _a(a), _b(b) {}
     int _a, _b;
public:
     class B
     {
     public:
         void foo()
         {
             A obj_A(12, 21);
             cout << obj_A._a << "\t" << obj_A._b << endl;
         }
     };
};

int main()
{
     A::B obj_B;
     obj_B.foo();
     cin.get();
     return 0;
}

Этот hello_world без каких-либо предупреждений о неповиновении стандарту компилируется в Visual Studio 2005 и работает(правильно:)). Но так лучше не делать.

Что говорит стандарт по этому поводу?

The members of a nested class have no special access to members of an enclosing class, nor to classes or functions that have granted friendship to an enclosing class; the usual access rules (clause 11) shall be obeyed. The members of an enclosing class have no special access to member of a nested class; the usual access rules(clause 11) shall be obeyed.

И далее следует пример:
class E
{
     int x;
     class B { };
     class I
     {
         B b; // error: E::B is private
         int y;
         void f(E* p, int i)
         {
             p->x = i; // error: E::x is private
         }
     };
     int g(I* p)
     {
         return p->y; // error: I::y is private
     }
};

В итоге получается, что вижак-таки отклонился от стандарта... Однако, вторую часть о доступе к приватным данным внутри вложенного класса из включающего класса он соблюдает, и код, подобный тому, что приведен ниже не скомпилируется.
class A
{
     class B
     {
         B(int c, int d) : _c(c), _d(d) {}
         int _c, _d;
     };
public:
     void foo()
     {
         B obj_B(13, 31);
         cout << obj_B._c << "\t" << obj_B._d << endl;
     }
};

int main()
{
     A obj_A;
     obj_A.foo();
     cin.get();
     return 0;
}

четверг, 5 ноября 2009 г.

Разбор аргументов командной строки в C

Столкнулся с проблемой разбора аргументов командной строки при написании программы на языке С. Вручную делать не захотелось, а, как оказалось, и не требуется - для этого уже есть стандартные POSIX библиотеки. Попробовал getopt - инструмент оказался очень удобным в использовании, так что и сишникам хорошо живётся в этом плане!

Например, чтобы разобрать опцию help, нужно всего лишь следующее:

struct option opt[] = {
{"help", no_argument, 0, 'h'}
};
//...
while((c = getopt_long(argc, argv, "h", opt, &opt_ind)) != -1) {
switch(c) {
case 'h':
fprintf(stderr, "-h [ --help ]\t\t produce help message\n\n");
break;
//...

Хотя это и не единственный путь - здесь небольшой обзор ещё нескольких библиотек.

четверг, 6 августа 2009 г.

Оператор with в Python

Оператор 'with' используется для обвертки блока выполнения методами, определёнными менеджером контекста. Оператор позволяет скрыть общие try...except...finally паттерны для удобного переиспользования.

Интерфейс менеджера контекста в стандартной библиотеке python 2.5 уже реализуют:
- file
- thread.LockType
- threading.Lock
- threading.RLock
- threading.Condition
- threading.Semaphore
- threading.BoundedSemaphore

Список будет расширяться.

Пример использования оператора 'with':


#!/bin/env python
#-*- coding: utf-8 -*-

import threading

class Worker(object):
def __init__(self):
self.lock = threading.RLock()

def execute(self):
with self.lock:
print "thread safe work there"

if __name__ == '__main__':
worker = Worker()
# execute worker.execte() from many threads
# ...
worker.execute()


Примечания:
1. оператор 'with' появился c python 2.5
2. для использования 'with' в python 2.5 необходимо импортировать with_statement фичу из __future__ :

from __future__ import with_statement__
# using 'with'

С python 2.6 'with' работает из коробки :).

Дополнительная информация:
http://docs.python.org/reference/compound_stmts.html#with
http://www.python.org/dev/peps/pep-0343/
http://docs.python.org/reference/datamodel.html#context-managers

среда, 5 августа 2009 г.

Из двух 16-разрядных целых вещественное, 32 разряда.

Раз уж здесь есть советы по типу "sizeof()", то добавлю этот. Наверно интересен для программистов, знающих о C только по наслышке. :)

Задача проста: имеются два 16-ти разрядных целых числа (другими словами, 2 байта информации, которая вроде как число), нужно склеить из них 32-х разрядное, вещественное. Условия задачи, в общем, легко переформулируются, важен принцип решения. :)

Решение просто, как все в C:

int w1 = 62915, w2 = 16456;
ui32 vl = ((w2&0xffff)<<16) | w1&0xffff;
printf("TEST 00: %f\n",*(float*)&vl);

PS: не забывайте про BIG ENDIAN и LITTLE_ENDIAN. ;)

четверг, 25 июня 2009 г.

нечто очевидное

typedef struct some_struct
{
int dummy;
unsigned long long some_field[];
} some_type;

int main() {
int buf;
buf = (int)&((some_type*)0)->some_field[0];
printf("buf = (int)&((some_type*)0)->some_field[0];\n");
printf("sizeof(unsigned long long): %d, sizeof(int): %d, \
sizeof(some_type): %d\n",
sizeof(unsigned long long), sizeof(int),
sizeof(some_type));
printf("buf: %d\n\n", buf);

buf = (int)&((some_type*)0)->some_field[1];
printf("buf = (int)&((some_type*)0)->some_field[1];\n");
printf("sizeof(unsigned long long): %d, sizeof(int): %d, \
sizeof(some_type): %d\n",
sizeof(unsigned long long), sizeof(int),
sizeof(some_type));
printf("buf: %d\n\n", buf);

}


1. (some_type*)0 создали на стеке некий объект и преобразовали его к типу "указатель на структуру some_type". Т.о. по адресу памяти, на который указывает (some_type*)0 будет находится некий мусор, но память будет выделена;

[#########################################################]
[|########################################################]

2. ((some_type*)0)->some_field[1] тут произошло смещение на 1 позицию размером в sizeof(typeof(some_field));

[#########################################################]
[#################################|#######################]

3. &((some_type*)0)->some_field[1] взяли адрес полученной ячейки памяти. Фактически, уже тут мы получаем искомое - объем занимаемой памяти;

4. (int)&((some_type*)0)->some_field[1] компилятор перестал ругаться;

Вуаля, в выражении
(int)&((some_type*)0)->some_field[0]
нам не нужно знать типы структур и полей, чтобы вычислить размер памяти.

понедельник, 22 июня 2009 г.

Использование pthread_create в C++

Часто возникает необходимо создать поток, программируя на C++, с помощью стандартного вызова pthread_create(). На вход он, кроме всего прочего, принимает указатель на функцию, которая и будет работать в потоке. Однако, если мы пишем на C++, то нам необходимо запускать в потоке метод класса. Напрямую метод класса отдать на вход вызову pthread_create не получится.
Вот пример конструкции, которая позволит обойти это:

/* pthr_tst.h */
#ifndef PTHR_TST_H
#define PTHR_TST_H

class PTHR_TST;

void *testThr(PTHR_TST * p);

class PTHR_TST {
    public:
       PTHR_TST();
       ~PTHR_TST();
       int createThreads();

    protected:
       friend void *testThr(PTHR_TST * p);

};

#endif

/* pthr_tst.cpp */
#include
#include "pthr_tst.h"

PTHR_TST::PTHR_TST() {}
PTHR_TST::~PTHR_TST() {}

void * testThr(PTHR_TST *p) {
    PTHR_TST * t = p;
    // t->someMethod();
    // someMethod() будет работать в потоке
    delete t;
    return 0;
}

int createThreads() {
    /* тоже хинт. g_tattr используется для задания
    максимального используемого объема памяти потоком */
    pthread_attr_t g_tattr;
    size_t ss=100*1024;
    pthread_attr_setstacksize(&g_tattr, ss);

    pthread_t testthr;
    pthread_create(&testthr, &g_tattr, (void *(*)(void*)) testThr, this);
}

понедельник, 1 июня 2009 г.

О размерности массива [C++]

Бывают случаи, когда необходимо узнать размерность массива. Делается это довольно просто:

int Mas[] = {4, 8, 15, 16, 23, 42};
int Size_Array = sizeof(Mas) / sizeof(int);

В памяти элементы массива располагаются последовательно, а объем памяти выделяемой под массив определяется как произведение всех размерностей массива (общее число элементов) умноженное на длину типа данных массива.