суббота, 31 мая 2014 г.
понедельник, 28 апреля 2014 г.
Converting png to icns programmatically
To solve it we need to get next libs:
I've attached the prepared solution, so use it and enjoy
- libicns
- libpng
- jasper
- zlib
I've attached the prepared solution, so use it and enjoy
вторник, 8 апреля 2014 г.
Pointers cheat sheet
Creating simple pointers
int* ptr = nullptr; // creating a null pointer
int val = 0; int* ptr = &val;// creating a pointer referencing to a variable *ptr = 90; // dereferencing pointer int* ptr2 = new int; // allocation for 1 uninitialized int variable *ptr2 = 100; // assign a value to address stored in ptr2 ptr2[0] = 100; // valid syntax but never do it this way :) int* arptr = new int; // allocating memory for 1 uninitialized int variable
Pointers and arrays
For 1d array, the name itself is a pointer pointing to the first element of the array:
int v[]{10,200,3000};
std::cout << (v == &v[0]); // output: 1
int* ptr2 = new int[10]; // allocating memory for array of 10 int's
From the code above we consider that for allocating array we need to have pointer first.
What about 2d array. The one is actually 1d array of 1d arrays, so we need to get array of... pointers for each array.
Since we need array of pointer then we need pointer to ... pointer!
int** pPtr = new int*[10]; // array of 10 pointers to int;
for (int i = 0; i < 10; ++i)
{
*(pPtr + i) = new int[10]; // for each of pointers allocate an array of int
}
We can increment/decrement pointer:
int v[]{10,200,3000};
int* ptr = v;
++ptr;
In this case pointer refers the next item rather than next address.
Assume v == 0x4, then ++ptr is 0x8, in other words: newSize = oldSize + sizeof(int)*1
ptr += 2; // newSize = oldSize + sizeof(int)*2We can subtract pointers:
int v[]{10,200,3000};
int* ptr = v;
int* ptr2 = &v[2];
int diff = ptr2 - ptr;
To get an array element you can use subscription syntax or "pointer plus offset" one:
int v[]{10,200,3000};
int* ptr = v;
*(ptr + 1) = 100; // ptr[1] = 100; which is the same as v[1] = 100
// for 2d arrays, let's create array 10x10
int** pPtr = new int*[10];
for (int i = 0; i < 10; ++i)
{
*(pPtr + i) = new int[10];
}
*(*(pPtr + 2) + 3) = 100;
std::cout << pPtr[2][3];
output: 100
the same for 3d, 4d, ...
int (*arrPtr)[10]; // pointer to an array of 10 elementscompare:
int *arrPtr[10]; // array of 10 poiters to int
Pointers and constants
const int cVar = 20; int* const constPtr = new int(12); // const pointer to an integer variable int const* ptrToIntConst = &cVar; // pointer to an integer constant const int* ptrToIntConst2 = &cVar; // pointer to an integer constant const int *const constPtrToIntConst = new int(12); // const pointer to an integer const (don't forget to assign an initial value) const int *const constPtrToIntConst2 = &cVar; // const pointer to an integer const int const *const constPtrToIntConst3 = new int(12);// const pointer to an integer const (don't forget to assign an initial value) int const *const constPtrToIntConst4 = &cVar;// const pointer to an integer const
Pointers to functions
int add( int a, int b)
{
return a + b;
}
int main(int argc, char* argv[])
{
int (*fPtr)(int x, int y) = add; // or int (*fPtr)(int x, int y) = &add;
fPtr(10,20);
return 0;
}
We can create a "pointer to function" using typedef
typedef int(*addFPtr)(int p1, int p2);
int add(int a, int b)
{
return a + b;
}
int main()
{
addFPtr afp = add;
int result = afp(1, 2);
return 0;
}
Pointers to functions can be passed to or return from a function like other types:
int getIntResult(addFPtr f, int a, int b)
{
return f(a, b);
}
void main()
{
addFPtr afp = add;
int result = getIntResult(afp, 7, 8);
return 0;
}
Pointers to member functions
class TestPointers
{
public:
int foo(int a){ return a + 2; }
int foo(int a) const { return a + 2; }
};
typedef int (TestPointers::*fooptr)(int);
typedef int (TestPointers::*constfooptr)(int) const;
int main()
{
TestPointers testPtrs;
fooptr ptr1 = &TestPointers::foo; // '&' here is necessary
int result = (testPtrs.*ptr1)(12);
constfooptr constPtr1 = &TestPointers::foo;
const TestPointers constTestPtrs;
result = (constTestPtrs.*constPtr1)(12);
return 0;
}
суббота, 29 марта 2014 г.
Automatical prompting admin privileges launching an application on Mac os
If your application requires admin privileges and you don't want the user run it from command line with sudo then you can use Apple script to achieve it
First create an apple dropplet with the code below
on run
Now after you double click the droplet you'll get the next
First create an apple dropplet with the code below
on run
on run
set appAlias to POSIX path of (path to resource "CamelAudioInstaller.app")
set cmnd to appAlias & "Contents/MacOS/CamelAudioInstaller"
#display dialog "You're going to launch the app without parameters" & cmnd buttons {"Ok"}
do shell script cmnd with administrator privileges
end run
on open this_item
set csPath to POSIX path of this_item
set appAlias to POSIX path of (path to resource "CamelAudioInstaller.app")
set cmnd to appAlias & "Contents/MacOS/CamelAudioInstaller " & csPath
#display dialog "You're going to launch " & cmnd buttons {"Ok"}
do shell script cmnd with administrator privileges
end open
Now after you double click the droplet you'll get the next
пятница, 1 ноября 2013 г.
Adding an additional path for finding dll in Visual Studio
Imagine that your application requires any dll. If the dll path is not in the $(PATH) or system32 or the application folder then you get the error right after starting:
To point out where to search the dll go to Project Properties->Debugging->Enviroment and change this values as
PATH=MySpecialDirectory/To/Search/Dll/$(PATH)
четверг, 17 октября 2013 г.
Getting current user's desktop path on Mac OS
The simplest solution is
But in the case of running the application in sudo mode it gives us the root's desktop path. To fix it use
NSArray* paths = NSSearchPathForDirectoriesInDomains(NSDesktopDirectory, NSUserDomainMask, YES ); NSString* theDesktopPath = [paths objectAtIndex:0];
But in the case of running the application in sudo mode it gives us the root's desktop path. To fix it use
NSString* home = [[[NSProcessInfo processInfo] environment] objectForKey:@"HOME"]; NSString *pathToDesktop = [NSString stringWithFormat:@"%@/Desktop", home];
суббота, 24 августа 2013 г.
How to build 32-bit curl and openssl on 64-bit Mac OS X
First let's donwload and build openssl:
OPENSSL_VERSION="1.0.1e" mkdir -p ~/packages/tmp_src cd ~/packages/tmp_src curl http://www.openssl.org/source/openssl-$OPENSSL_VERSION.tar.gz > openssl-$OPENSSL_VERSION.tar.gz tar -xvzf openssl-$OPENSSL_VERSION.tar.gz mv openssl-$OPENSSL_VERSION openssl_i386 cd openssl_i386 ./Configure darwin-i386-cc make -j 8 make install cd ../To make sure that we built 32-bit versions:
lipo -info /path/to/libssl.a lipo -info /path/to/libcrypto.aIt should tell us that architecture is i386 Then do the same for curl
CURL_VERSION="7.29.0" curl http://curl.haxx.se/download/curl-$CURL_VERSION.tar.gz > curl-$CURL_VERSION.tar.gz tar xzvf curl-$CURL_VERSION.tar.gz mv curl-$CURL_VERSION curl_i386 cd curl_i386 ./configure --with-ssl=/usr/local/ssl --prefix=/Users/USERNAME/packages/tmp_src/curl_i386 --disable-shared --host=i386 --disable-ldap --without-zlib make -j 8 #make install
четверг, 4 апреля 2013 г.
Using alternative tokens in Visual Studio
There's an interesting thing in C++ called alternative tokens

For using tokens in Visual Studio you should:

For using tokens in Visual Studio you should:
- - For the tokens from 1st column compile with /Za flag (Configuration properties->C/C++->Language->Disable language Extensions = Yes)
- - For the tokens from 2nd and 3rd columns include <iso646.h>
суббота, 23 марта 2013 г.
Libcurl and linker errors
If you were to install libcurl on Windows you could come across a bulk of linker errors such as
Error 1176 error C2065: '_SH_DENYNO' : undeclared identifier c:\program files (x86)\microsoft visual studio 10.0\vc\include\xiosbaseHow to fix it? Go to curl sources, rename share.h to share_curl.h for instance and replace all occurences of
#include "share.h"with
#include "share_curl.hThen recompile libcurl.
воскресенье, 18 ноября 2012 г.
How to install Homebrew on Mac OS
Homebrew is the missing package manager for OS X
To install it type:
To install it type:
ruby -e "$(curl -fsSL https://raw.github.com/mxcl/homebrew/go)"
воскресенье, 27 февраля 2011 г.
Отличия ADDR и OFFSET
Проясним момент насчет различия операторов ADDR и OFFSET в программах, написанных на Ассемблере.
Первоочередное назначение данных операторов - это получение адреса переменной в памяти.
В программах на ассемблере могут присутствовать как локальные так и глобальные переменные.
Глобальные переменные существуют в памяти на протяжении всего времени выполнения программы, локальные - только во время выполнения процедуры, в которой они определены, и удаляются как только она завершит работу. Поэтому адреса глобальных переменных известны уже на стадии ассемблирования, тогда как адреса локальных переменных в стеке станут известны только во время выполнения программы, а точнее при выполнении процедуры, в которой эти переменные объявлены.
Что касается операторов, то OFFSET вычисляет адрес уже размещенных к началу выполнения программы переменных, а это значит что данный оператор можно использовать для вычисления адресов только глобальных переменных.
С помощью оператора ADDR можно получить адрес локальной переменной.
Но почему так получается, что с помощью ADDR можно узнать адрес локальной пременной, а с помощью OFFSET - нельзя. Дело в том, что ассемблер, встретив оператор ADDR, использующийся с локальной переменной, генерирует последовательность инструкций
Первоочередное назначение данных операторов - это получение адреса переменной в памяти.
В программах на ассемблере могут присутствовать как локальные так и глобальные переменные.
Глобальные переменные существуют в памяти на протяжении всего времени выполнения программы, локальные - только во время выполнения процедуры, в которой они определены, и удаляются как только она завершит работу. Поэтому адреса глобальных переменных известны уже на стадии ассемблирования, тогда как адреса локальных переменных в стеке станут известны только во время выполнения программы, а точнее при выполнении процедуры, в которой эти переменные объявлены.
Что касается операторов, то OFFSET вычисляет адрес уже размещенных к началу выполнения программы переменных, а это значит что данный оператор можно использовать для вычисления адресов только глобальных переменных.
С помощью оператора ADDR можно получить адрес локальной переменной.
Но почему так получается, что с помощью ADDR можно узнать адрес локальной пременной, а с помощью OFFSET - нельзя. Дело в том, что ассемблер, встретив оператор ADDR, использующийся с локальной переменной, генерирует последовательность инструкций
lea eax, localVar push eaxну а инструкция lea уже делает свое дело. Т.к. ADDR превращается в выше указанную посдедовательность инструкций, данный оператор можно использовать только с директивой invoke для передачи адреса переменной в качестве параметра. Присваивание регистру полученного таким способом адреса невозможно. ADDR можно так же использоваться и с глобальными переменными. В этом случае ассемблер сгенерирует
push offset globalVarИ в отличие от OFFSET, ADDR не умеет вычислять смещения меток, определенных далее в коде.
mov eax, offset l1 ; так можно invoke Foo, ADDR l1; так нельзя l1: xor ebx, ebx invoke Foo, ADDR l1; так можно
среда, 26 января 2011 г.
EFLAGS. Перенос (CF) и переполнение (OF)
Есть в процессоре (будем говорить об архитектуре Intel) такой замечательный регистр - EFLAGS или регистр флагов. Данный 32-битный регистр содержит группу статусных флагов, группу флагов контроля и группу системных флагов. Как правило, в учебниках по архитектуре компьютера или ассемблеру присутствует довольно сухое описание, для чего определенный флаг предназначен, и еще чаще отсутствуют примеры, связанные с данным флагом. Рассмотрим два важных флага - флаг переноса (carry flag - CF) и флаг переполнения (overflow flag - OF) и постараемся более подробно осветить эту тему, чем в обычно это делают в учебниках.
Из руководства "Intel Software developer`s manual vol.1" (перевод):
CF - устанавливается, если в результате арифметической операции произошло переполнение разрядной сетки либо заем из старшего бита. Флаг свидетельствует о переполнении для беззнаковых чисел.
Будем рассматривать все на примере 8-битных чисел/регистров.
С переполнением разрядной сетки все достаточно просто. Диапазон беззнаковых чисел, которые могут быть представлены 8 битами - это 0...255. Следовательно переполнение наступит, если результатом выполнения операции будет число больше 255.
В данном случае число не помещается в 8 бит, и возникает переполнение.
Рассмотрим числа 8 и 6. Что будет если из 6 вычесть 8? В двоичном представлении 6 имеет вид 00000110, 8 - 00001000. При поразрядном вычитании в 4 бите произойдет заем, который породит заем из 5,6,7,8 битов. Т.е. в итоге происходит заем из старшего бита, а это значит что флаг переноса устанавливается.
Для чего в данном случае устанавливается флаг. Дело в том, что результат получается отрицательным, что логично, но это неверно для беззнаковых чисел, которые по определению не могут иметь отрицательного значения. Т.е. результат опять не входит в диапазон 0...255 Из этого можно сделать вывод - если происходит вычитание большего числа из меньшего, то флаг переноса будет установлен, т.к. в этом случае происходит заем из старшего бита.
А при сложении отрицательного и положительного (минус на минус дает плюс) и получая по сути тот же результат, флаг переноса устанавливаться не будет, несмотря на то, что результат является слишком малым и не входит в диапазон 0...255. Дело в том, что отсутствует заем из старшего бита.
Флаг переноса полезен при работе с большими числами, не помещающимися в 32-разрядный регистр. Например, если требуется сложить 2 числа, каждое из которых расположено в паре 32-рязрядных регистров.
Рассмотрим флаг переполнения.
Из руководства "Intel Software developer`s manual vol.1" (перевод):
Флаг OF устанавливается, если число - слишком большое положительное, либо слишком маленькое отрицательное. Флаг свидетельствует о переполнении для знаковых чисел.
Диапазон знаковых чисел в 8-битном случае -128...127, т.е., как сказано в руководстве, 2 варианта:
Первый случай:
Второй случай:
Но что такое "знаковые числа"? Как процессор понимает, когда используется знаковое, а когда - беззнаковое число. На самом деле процессор не знает этого. Он сразу предполагает оба случая и в соответствии с этим выставляет флаги.
Из руководства "Intel Software developer`s manual vol.1" (перевод):
CF - устанавливается, если в результате арифметической операции произошло переполнение разрядной сетки либо заем из старшего бита. Флаг свидетельствует о переполнении для беззнаковых чисел.
Будем рассматривать все на примере 8-битных чисел/регистров.
С переполнением разрядной сетки все достаточно просто. Диапазон беззнаковых чисел, которые могут быть представлены 8 битами - это 0...255. Следовательно переполнение наступит, если результатом выполнения операции будет число больше 255.
clc
mov al, 250
mov bl, 10
add al, bl ; должно быть 260, но на деле в al 4
В данном случае число не помещается в 8 бит, и возникает переполнение.
Рассмотрим числа 8 и 6. Что будет если из 6 вычесть 8? В двоичном представлении 6 имеет вид 00000110, 8 - 00001000. При поразрядном вычитании в 4 бите произойдет заем, который породит заем из 5,6,7,8 битов. Т.е. в итоге происходит заем из старшего бита, а это значит что флаг переноса устанавливается.
clc
mov al, 6
mov bl, 8
sub al, bl ; теперь в al -2
Для чего в данном случае устанавливается флаг. Дело в том, что результат получается отрицательным, что логично, но это неверно для беззнаковых чисел, которые по определению не могут иметь отрицательного значения. Т.е. результат опять не входит в диапазон 0...255 Из этого можно сделать вывод - если происходит вычитание большего числа из меньшего, то флаг переноса будет установлен, т.к. в этом случае происходит заем из старшего бита.
А при сложении отрицательного и положительного (минус на минус дает плюс) и получая по сути тот же результат, флаг переноса устанавливаться не будет, несмотря на то, что результат является слишком малым и не входит в диапазон 0...255. Дело в том, что отсутствует заем из старшего бита.
mov al, -79
mov bl, 49
add al, bl ; CF = 0
Но если взять вместо -79 число -49, то флаг переноса установится, т.к. -49 для беззнаковых чисел равно 207, а 207+49=256, что является переполнением 8 бит.Флаг переноса полезен при работе с большими числами, не помещающимися в 32-разрядный регистр. Например, если требуется сложить 2 числа, каждое из которых расположено в паре 32-рязрядных регистров.
Рассмотрим флаг переполнения.
Из руководства "Intel Software developer`s manual vol.1" (перевод):
Флаг OF устанавливается, если число - слишком большое положительное, либо слишком маленькое отрицательное. Флаг свидетельствует о переполнении для знаковых чисел.
Диапазон знаковых чисел в 8-битном случае -128...127, т.е., как сказано в руководстве, 2 варианта:
- Число слишком мало для представления в виде 8-битного знакового
- Число слишком велико для представления в виде 8-битного знакового
Первый случай:
mov al, 117
mov bl, 92
add al, bl ;
В итоге имеем -47 вместо 209 из-за переноса в знаковый бит, т.е. число 209 выходит за допустимый диапазон сверху.Второй случай:
mov al, -20
mov bl, -118
add al, bl ;
Складываем 2 отрицательных числа и получаем тем не менее положительную сумму (118 вместо -138), т.к. -138 выходит за допустимый диапазон снизу.Но что такое "знаковые числа"? Как процессор понимает, когда используется знаковое, а когда - беззнаковое число. На самом деле процессор не знает этого. Он сразу предполагает оба случая и в соответствии с этим выставляет флаги.
пятница, 14 января 2011 г.
Многопоточное программирование. 3. Проблема производителя и потребителя
Проблема производителя и потребителя, известная так же как проблема ограниченного буфера, является одной из классических задач многопроцессной синхронизации.
Рассмотрим упрощенный случай с двумя процессами. Первый процесс - Производитель (Producer), второй - Потребитель (Consumer).
Процессы используют общий буфер: Производитель помещает информацию в буфер, в то время как Потребитель извлекает ее, независимо от Производителя. Для Производителя всегда должно присутствовать незанятое место (слот) для записи информации, тогда как для Потребителя информация для извлечения должна присутствовать всегда, т.е. как минимум один непустой слот.
Производитель надеется на Потребителя, что тот предоставит свободное место для вставки очередной порции информации, а Потребитель надеется на Производителя, что тот обеспечит информацию для извлечения.
В таком случае Производитель и Потребитель каким-то образом должны взаимодействовать между собой, чтобы знать, что очередная вставка/извлечение будет безопасной для работающего в данный момент одного из процессов.
На первый взгляд решение, представленное ниже, абсолютно верное:
MAX_SIZE - максимальный размер буфера
Производитель сначала проверяет, не полон ли буфер, и если буфер полон, то Производитель засыпает, если не полон, то помещает информацию, увеличивает счетчик занятых слотов, и проверяет, не был ли буфер пусто до этого, если был пуст - значит Потребитель спит, и его надо разбудить.
Потребитель же наоборот, засыпает, когда буфер оказывается пустым. Если же после извлечения очередного элемента, Потребитель узнает, что до этого буфер был полным, значит Производитель спит и его надо разбудить.
Но в жизни все не так :-)
Представим, что буфер полон и работает Производитель. Осуществляется проверка на полноту буфера, и внезапно после выполнения строки (1), т.е. когда стало уже известно что надо заснуть, произошло переключение процесса. Теперь работает Потребитель. Он исправно извлекает информацию. Очистив первый слот, Потребитель понимает что буфер был полон, и нужно разбудет спящего, как ему кажется, производителя. Потребитель посылает сигнал пробуждения, и очищает буфер до конца, после чего засыпает. После этого происходит переключение на процесс Производителя, который не спал, а только собирался, и теперь он готов осуществить свою мечту - немного поспать. И засыпает. Все спят. Взаимоблокировка или deadlock.
А проблема, как уже многие догадались, состоит в утраченном вызове активизации процесса. Т.е. Производитель не спал, а его будили. К аналогичному результату привело бы рассмотрение ситуации, если бы буфер оказался пустым, и Потребитель решил бы уснуть, но "не успел".
Итак. Попробуем устранить проблему утраченного сигнала. Для этого будем использовать семафоры.
Cемафор - переменная, как правило целочисленная, используется для синхронизации доступа множества параллельно работающих процессов/потоков к общему ресурсу. Между семафором и обыкновенной численной переменной есть различия:
Вообще говоря, мьютекc и двоичный семафор это не одно и то же. Правильнее будет рассуждать, что двоичный семафор может быть использован как мьютекс, но не наоборот.
Двоичный семафор будет использоваться для управления доступом к буферу (чтобы в данный момент только один из потоков, а их может быть очень много, имел доступ к критической секции)
Подсчитывающие семафоры буду использоваться для подсчета свободных и занятых слотов буфера
Рассмотрим упрощенный случай с двумя процессами. Первый процесс - Производитель (Producer), второй - Потребитель (Consumer).
Процессы используют общий буфер: Производитель помещает информацию в буфер, в то время как Потребитель извлекает ее, независимо от Производителя. Для Производителя всегда должно присутствовать незанятое место (слот) для записи информации, тогда как для Потребителя информация для извлечения должна присутствовать всегда, т.е. как минимум один непустой слот.
Производитель надеется на Потребителя, что тот предоставит свободное место для вставки очередной порции информации, а Потребитель надеется на Производителя, что тот обеспечит информацию для извлечения.
В таком случае Производитель и Потребитель каким-то образом должны взаимодействовать между собой, чтобы знать, что очередная вставка/извлечение будет безопасной для работающего в данный момент одного из процессов.
На первый взгляд решение, представленное ниже, абсолютно верное:
#define MAX_SIZE 100
int count = 0;
Producer()
{
while (TRUE)
{
item = ProduceItem();
if (count == MAX_SIZE) // (1) если буфер полон
sleep(); // (2)
InsertItem(item);
++count;
if (count == 1)// был ли буфер пуст
wakeup(Consumer);
}
}
Consumer()
{
while(TRUE)
{
if (count == 0) // (3) если буфер пуст
sleep(); // (4)
item = RemoveItem();
--count;
if (count == MAX SIZE - 1) // был ли буфер полон
wakeup(Producer);
ConsumeItem(item);
}
}
count - количество слотов информации (или товаров, кому как) в буфере.MAX_SIZE - максимальный размер буфера
Производитель сначала проверяет, не полон ли буфер, и если буфер полон, то Производитель засыпает, если не полон, то помещает информацию, увеличивает счетчик занятых слотов, и проверяет, не был ли буфер пусто до этого, если был пуст - значит Потребитель спит, и его надо разбудить.
Потребитель же наоборот, засыпает, когда буфер оказывается пустым. Если же после извлечения очередного элемента, Потребитель узнает, что до этого буфер был полным, значит Производитель спит и его надо разбудить.
Но в жизни все не так :-)
Представим, что буфер полон и работает Производитель. Осуществляется проверка на полноту буфера, и внезапно после выполнения строки (1), т.е. когда стало уже известно что надо заснуть, произошло переключение процесса. Теперь работает Потребитель. Он исправно извлекает информацию. Очистив первый слот, Потребитель понимает что буфер был полон, и нужно разбудет спящего, как ему кажется, производителя. Потребитель посылает сигнал пробуждения, и очищает буфер до конца, после чего засыпает. После этого происходит переключение на процесс Производителя, который не спал, а только собирался, и теперь он готов осуществить свою мечту - немного поспать. И засыпает. Все спят. Взаимоблокировка или deadlock.
А проблема, как уже многие догадались, состоит в утраченном вызове активизации процесса. Т.е. Производитель не спал, а его будили. К аналогичному результату привело бы рассмотрение ситуации, если бы буфер оказался пустым, и Потребитель решил бы уснуть, но "не успел".
Итак. Попробуем устранить проблему утраченного сигнала. Для этого будем использовать семафоры.
Cемафор - переменная, как правило целочисленная, используется для синхронизации доступа множества параллельно работающих процессов/потоков к общему ресурсу. Между семафором и обыкновенной численной переменной есть различия:
- Будучи созданным, семафору можно присвоить любое значение (для counting semaphore), но операции, которые могут работать с семафором, ограничиваются лишь инкрементом и декрементом
- Когда процесс/поток уменьшает значение семафора, и результат становится отрицательным, то он блокирует сам себя, и не может продолжать выполнение, пока другой процесс/поток не увеличит значение семафора
- Если процесс/поток инкрементирует значение семафора, и есть ожидающие процессы, то один из них разблокируется
- Counting semaphore (подсчитывающий)
- Binary semaphore (двоичный, или мьютекс)
Вообще говоря, мьютекc и двоичный семафор это не одно и то же. Правильнее будет рассуждать, что двоичный семафор может быть использован как мьютекс, но не наоборот.
Двоичный семафор будет использоваться для управления доступом к буферу (чтобы в данный момент только один из потоков, а их может быть очень много, имел доступ к критической секции)
Подсчитывающие семафоры буду использоваться для подсчета свободных и занятых слотов буфера
#define MAX_SLOTS 100
typedef int semaphore;
semaphore mutex = 1;
semaphore empty = N; // подсчет пустых слотов в буфере
semaphore full = 0; // подсчет заполненных слотов буфере
Producer()
{
int item;
while (TRUE)
{
item = ProduceItem();
down(empty); // (1)
down(mutex); // (2)
EnterItem(item);
up(mutex); // (3)
up(full); // (4)
}
}
Consumer()
{
int item;
while (TRUE)
{
down(full); // (5)
down(mutex); // (6)
item = RemoveItem();
up(mutex); // (7)
up(empty); // (8)
ConsumeItem(item);
}
}
Итак, mutex отвечает за доступ одного процесса/потока в данный момент времени. Разберемся с empty и full. Пусть какое-то время программа работает, затем буфер заполняется полностью. Пусть в это время работает Производитель. Если буфер заполнен полностью, то empty = 0, это значит, что команда down(empty) приведет к блокировке Производителя (вспоминаем пункт 2 о семафорах). Далее работает Потребитель, разгружает слоты, регулирует семафоры количества пустых и полных слотов (количество пустых растет), и после первого очищения слота, Производитель может быть разблокирован, т.к. значение семафора увеличилось. Допустим произошло переключени на Производителя. Он продолжает выполнение со строки (2), заходит в критическую секцию, заполняет слот, выходит и увеличивает количество заполненных ячеек, тем самым позволяя Потребителю (если тот заблокирован) разблокироваться. В том и разница, что в данном случае Производитель не производит насильную разблокировку Потребителю, не задумываясь, надо ли это ему, а лишь создает условия, необходимые для разблокировки в случае, если таковая потребуется. Читателю предлагается проверить оставшиеся три граничные ситуации:- Буфер полон, работает Потребитель
- Буфер пуст, работает Производитель
- Буфер пуст, работает Потребитель
Ярлыки:
Многопоточность
,
Операционные системы
,
Собеседование
четверг, 21 октября 2010 г.
Многопоточное программирование. 2. Алгоритм Петерсона... чуть более подробно. ч.2. Случай N процессов
В прошлый раз был рассмотрен алгоритм Петерсона для взаимного исключения двух процессов. На этот раз рассмотрим алгоритм Петерсона, обощенный на N параллельно выполняющихся процессов.
Код алгоритма:
А теперь немного теории.
Алгоритм Петерсона для N процессов по сути тот же самый, что и для случая двух процессов, однако перед входом в критическую процесс должен преодолеть (n-1) стадий, т.н. "состязания". Количество стадий равно (n-1), потому что на каждой стадии будет оставаться хотя бы один процесс, а остальные будут переходить на следующую стадию, т.е. не более (n-j) процессов могут покинуть стадию под номером j. В конце концов у нас останется только два процесса, один из которых пройдет в критическую секцию. Два процесса будут как раз на (n-1)-й стадии. Преодоление каждой стадии напоминает версию для двух процессов, где один из процессов временно устраняется.
stage[process] - номер стадии, в которой находится процесс с идентификатором process
turn[i] = process - идентификатор процесса process, зашедшего последним в стадию под номером i
Рассмотрим поведение процессов на рисунке.
Пусть поочередно все 4 процесса попали на первую стадию. Пусть последним оказался процесс 1.
Происходит "опрос" остальных процессов на наличие их на той же самой стадии, где находится работающий процесс, либо на стадиях с бОльшим номером. Пусть вследствие прерываний до цикла, где происходит опрос, добрался процесс 2. В этом случае условие
Несмотря на то, что на данной стадии кроме процесса 2 имеются еще процессы, процесс 2 НЕ является последним перешедшим на данную стадию (мы условились, что последним будет процесс 1). Стало быть процесс 2 проходит на следующую стадию. И если так случится, что прерываний не будет, и его никто не "догонит", то процесс 2 благополучно войдет в критическую секцию, затем выйдет из нее, присвоит переменной interested[process] значение FALSE, что будет означать что данный процесс находится на стадии 0, т.е. на "старте", ну или на самой "низшей" стадии.
Если же переключению процессов случаются примерно через равные промежутки времени, то всегда для последнего процесса, зашедшего на данную стадию, условие
Таким образом на последующую стадию переходит на один процесс меньше, и так пока не останется двух процессов, после чего один из двух войдет в критическую секцию. Второй из двух войдет в критическую секцию после выхода первого, потому что (stage[j] >= i) будет равно FALSE. А если второго процесса кто либо "догонит", тем не менее (turn[i] == process) будет равно FALSE, и процесс все равно войдет в критическую секцию.
Код алгоритма:
#define FALSE 0
#define N 10
int turn[N];
int stage[N + 1];
void enterRegion(int process)
{
for (int i = 1; i <= N - 1; i++) // перебор стадий
{
stage[process] = i;
turn[i] = process;
for (int j = 1; j <= N; j++) // "опрос" остальных процессов
{
if (j == process)
continue;
while (stage[j] >= i && turn[i] == process);
}
}
}
void leaveRegion(int process)
{
stage[process] = FALSE;
}
Для простоты и улучшения читабельности кода не будем рассматривать процесс с индексом 0, так же не будем рассматривать стадию с индексом 0, точнее стадия с индексом 0 будет своеобразным "стартом".А теперь немного теории.
Алгоритм Петерсона для N процессов по сути тот же самый, что и для случая двух процессов, однако перед входом в критическую процесс должен преодолеть (n-1) стадий, т.н. "состязания". Количество стадий равно (n-1), потому что на каждой стадии будет оставаться хотя бы один процесс, а остальные будут переходить на следующую стадию, т.е. не более (n-j) процессов могут покинуть стадию под номером j. В конце концов у нас останется только два процесса, один из которых пройдет в критическую секцию. Два процесса будут как раз на (n-1)-й стадии. Преодоление каждой стадии напоминает версию для двух процессов, где один из процессов временно устраняется.
stage[process] - номер стадии, в которой находится процесс с идентификатором process
turn[i] = process - идентификатор процесса process, зашедшего последним в стадию под номером i
Рассмотрим поведение процессов на рисунке.
Пусть поочередно все 4 процесса попали на первую стадию. Пусть последним оказался процесс 1.
Происходит "опрос" остальных процессов на наличие их на той же самой стадии, где находится работающий процесс, либо на стадиях с бОльшим номером. Пусть вследствие прерываний до цикла, где происходит опрос, добрался процесс 2. В этом случае условие
while (stage[j] >= i && turn[i] == process)не выполнится.
Несмотря на то, что на данной стадии кроме процесса 2 имеются еще процессы, процесс 2 НЕ является последним перешедшим на данную стадию (мы условились, что последним будет процесс 1). Стало быть процесс 2 проходит на следующую стадию. И если так случится, что прерываний не будет, и его никто не "догонит", то процесс 2 благополучно войдет в критическую секцию, затем выйдет из нее, присвоит переменной interested[process] значение FALSE, что будет означать что данный процесс находится на стадии 0, т.е. на "старте", ну или на самой "низшей" стадии.
Если же переключению процессов случаются примерно через равные промежутки времени, то всегда для последнего процесса, зашедшего на данную стадию, условие
while (stage[j] >= i && turn[i] == process)не будет выполняться по той простой причине, что он последний: (turn[i] == process).
Таким образом на последующую стадию переходит на один процесс меньше, и так пока не останется двух процессов, после чего один из двух войдет в критическую секцию. Второй из двух войдет в критическую секцию после выхода первого, потому что (stage[j] >= i) будет равно FALSE. А если второго процесса кто либо "догонит", тем не менее (turn[i] == process) будет равно FALSE, и процесс все равно войдет в критическую секцию.
Ярлыки:
Многопоточность
,
Операционные системы
,
Собеседование
суббота, 2 октября 2010 г.
Многопоточное программирование. 1. Алгоритм Петерсона... чуть более подробно
Разберем сегодня алгоритм Петерсона. Посвящается всем начинающим, продолжающим,
прогулявшим пары по операционным системам и тем, до кого не дошло сразу. Алгоритм
Петерсона позволяет добиться взаимного исключения двух процессов. Рассмотрим 3 варианта поведения двух процессов при возникновении прерываний в трех различных точках. Также в каждом из рассмотренных случаев будем допускать прерывание при первом входе в критическую секциию одного из процессов
#define FALSE 0
#define TRUE 1
#define N 2 // количество процессов
int turn; // чья очередь
int interested[N]; // изначально значения равны 0
void enter_region(int process) // номер процесса - 0 или 1
{
int other;
other = 1 - process; // точка 1
interested[process] = TRUE; // точка 2 (заинтересованный процесс)
turn = process; // точка 3
while(turn == process && interested[other] == TRUE); // активное ожидание
}
void leave_region(int process) // номер процесса - 0 или 1
{
interested[process] = 0; // признак выхода из критической секции
}
Итак, turn и interested[] являются глобальными переменными, прочиепеременные - локальные.
Случай 1: переключение процессов возникло в точке 1
| Процесс 0 | Процесс 1 |
| other = 1 | |
| Прерывание процесса 0, переключение на процесс 1 | |
| other = 0, interested[1] = TRUE, turn = 1 | |
| Проверка условий turn == 1 (true) и interested[0] == TRUE (false) | |
| Вход в критическую секцию | |
| Прерывание процесса 1, переключение на процесс 0 | |
| interested[0] = TRUE, turn = 0 | |
| Проверка условий turn == 0 (true) и interested[1] == TRUE (true) | |
| Вход в бесконечный цикл, переключение на процесс 1 | |
| Процесс 1 выходит из критической секции и устанавливает interested[1] = FALSE | |
| Прерывание процесса 1, переключение на процесс 0 | |
| Проверка условий turn == 0 (true) и interested[1] == TRUE (false) | |
| Вход в критическую секцию |
Случай 2: переключение процессов возникло в точке 2
| Процесс 0 | Процесс 1 |
| other = 1, interested[0] = TRUE | |
| Прерывание процесса 0, переключение на процесс 1 | |
| other = 0, interested[1] = 1, turn = 1 | |
| Проверка условий turn == 1 (true) и interested[0] == TRUE (true) | |
| Вход в бесконечный цикл, переключение на процесс 0 | |
| turn = 0 | |
| Проверка условий turn == 0 (true) и interested[1] == 1 (true) | |
| Вход в бесконечный цикл, преключение на процесс 1 | |
| Проверка условий turn == 1 (false) и interested[0] == 1 (true) | |
| Вход в критическую секцию | |
| Прерывание процесса 1, переключение на процесс 0 | |
| Проверка условий turn == 0 (true) и interested[1] == 1 (true) | |
| Вход в бесконечный цикл, преключение на процесс 1 | |
| Процесс 1 выходит из критической секции и устанавливает interested[1] = FALSE | |
| Прерывание процесса 1, переключение на процесс 0 | |
| Проверка условий turn == 0 (true) и interested[1] == 1 (false) | |
| Вход в критическую секцию |
Случай 3: переключение процессов возникло в точке 3
| Процесс 0 | Процесс 1 |
other = 1, interested[0] = TRUE, turn = 0 | |
| Прерывание процесса 0, переключение на процесс | |
| other = 0, interested[1] = TRUE, turn = 1 | |
| Проверка условий turn == 1 (true) и interested[0] == TRUE (true) | |
| Вход в бесконечный цикл, преключение на процесс 0 | |
| Проверка условий turn == 0 (false) и interested[1] == TRUE (true) | |
| Вход в критическую секцию | |
| Прерывание процесса 0, переключение на процесс | |
| Проверка условий turn == 1 (true) and interested[0] == TRUE (true) | |
| Вход в бесконечный цикл, преключение на процесс 0 | |
| Процесс 0 выходит из критической секции и устанавливает interested[0] = FALSE | |
| Прерывание процесса 0, переключение на процесс | |
| Проверка условий turn == 1 (true) и interested[0] == TRUE (false) | |
| Вход в критическую секцию |
Продолжение следует...
Ярлыки:
Многопоточность
,
Операционные системы
,
Собеседование
среда, 29 сентября 2010 г.
Особенности выравнивания данных в структурах
Начинающим да и продолжающим программистам порой непонятно, почему в некоторых случаях размер структуры может быть больше, чем суммарный размер входящих в нее членов. Например:
struct Test
{
char var1; // 1 байт
short var2; // 2 байта
};
Как видно, суммарный размер структуры должен быть равен 3 байтам, на деле же оказывается вовсе не 3, а 4 байта.Именно такой результат возвратит нам sizeof(Test). Все дело в выравнивание, которое осуществляет компилятор.
Т.е. данные помещаются по адресам, кратным размерам своих типов - 2-байтовые данные буду находятся по адресу, кратному 2, 4-байтные - по адресу, кратному 4 и т.д. (выравнивание по умолчанию)
Выравнивание нужно, потому что с выровненными данными процессор работает более эффективно. Если процессор обращается к невыровненным данным, то сначала он считывает одну часть данных, затем считывает другую часть, после этого компонует и получает исходные данные. Все это сокращает производительность.
Для выравнивания между данными помещаются специальные байты отступа (padding)
Рассмотрим примеры (ОС Windows).
struct Test
{
unsigned char var1;
unsigned char var2;
unsigned char var3;
};
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
| char | char | char | ... | |||||
В итоге суммарный размер равен 3 байта.
Другой пример:
struct Test
{
unsigned char var1;
unsigned short var2;
};
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
| char | P | short | ... | |||||
struct Test
{
unsigned short var1;
unsigned char var2;
};
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
| short | char | P | ... | |||||
Допустим у нас есть такая структура:
struct Test
{
unsigned int var1;
unsigned char var2;
};
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
| int | char | P | P | P | ... | |||||
Если имеется структура
struct Test
{
unsigned int var1;
unsigned short var2;
unsigned char var3;
};
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
| int | short | char | P | ... | ||||||
Аналогично рассмотрим структуру вида
struct Test
{
double var1;
unsigned int var2;
unsigned short var3;
unsigned char var4;
};
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | ||||||
| double | int | short | char | P | ... | ||||||||||||||||||
Представим такую ситуацию:
struct Test
{
unsigned short var1;
unsigned char var2;
double var3;
};
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
| short | char | P | P | P | P | P | double | ... | ||||||||||
ВНИМАНИЕ! Данные результаты получены при опции компилятора
Struct member alignment = Default;
Для значений /zp1, /zp2, zp[n] результаты будут отличны от полученных.
вторник, 10 августа 2010 г.
Array name != pointer to the first array element?
from the standard (4.2.1):
An Array variable, defined as
Let's assume we have an array:
In this case the expression
thus if ar[10] is an array, and ar is a pointer, then if (*p)[10] is a pointer,
and p is a pointer to pointer
So we should write the next:
since p is associated with 10 int's array, then while incrementing
p will change to sizeof(int), rather than 10*sizeof(int).
An lvalue or rvalue of type 'array of N T' or 'array of unknown bound of T' can be converted to an rvalue of type 'pointer to T'. The result is a pointer to the first element of the array.I.e.:
An Array variable, defined as
T ar[N]; //array of N Tor
T ar[] = {val1, val2, val3}; //array of unknown bound of T
can be converted to an rvalue of type 'pointer to T'Let's assume we have an array:
int ar[10];if we want to pass it to a function
void Foo(int* ar);we should write next:
Foo(ar); // ar automatically converted to a pointer to the 1st elementOr
int ar[10]; int* p = ar; *p = 10; // equals ar[0] = 10; p+=2; *p = 20; // equals ar[2] = 20;We can also write the next:
int (*p)[10];Here p is a pointer to array of 10 int's (type is int (*)[10]). Exactly "pointer to array"!
In this case the expression
p = ar;won't compile. In the declaration
int (*p)[10];p is already a pointer(!),
thus if ar[10] is an array, and ar is a pointer, then if (*p)[10] is a pointer,
and p is a pointer to pointer
So we should write the next:
p = &ar;Despite ar and &ar have the same meaning.
since p is associated with 10 int's array, then while incrementing
p will change to sizeof(int), rather than 10*sizeof(int).
int ar[3][4];
for(int i=0; i < 3; i++)
{
for (int j=0; j < 4; j++)
{
ar[i][j]=i+j;
}
}
int (*p)[4];
p=ar;
++p; //advance to the next row
int a_2_1 = *(*(p+2)+1) // equals int a_2_1 = p[2][1]
четверг, 10 июня 2010 г.
Include guards is not all-healing
Once upon a time a got a linker error:
(MSVC2008):
But they are intended to only avoid the problem of double inclusion.
The error occurs when the linker finds two matches that collide.
In our case there are two object files - mainfile.obj and testing.obj.
Each of them has TestNS::namespaceData. A solution is not to define the variable in the header file. It´s needed to be defined in the source file and only declared in the header fileby use extern directive
//файл testing.h
#ifndef TESTING_H
#define TESTING_H
class MyClass
{
private:
int classData;
public:
MyClass();
~MyClass();
};
namespace TestNS
{
int namespaceData;
};
#endif
//файл testing.cpp
#include "Testing.h"
MyClass::MyClass()
{
//........
}
MyClass::~MyClass()
{
//........
};
//файл mainfile.cpp
#include "Testing.h"
int main()
{
TestNS::namespaceData = 0;
return 0;
}
(MSVC2008):
Error 1 error LNK2005: "int TestNS::namespaceData" (?namespaceData@TestNS@@3HA) already defined in Testing.obj mainfile.obj
Error 2 fatal error LNK1169: one or more multiply defined symbols found E:\test.exe
It seems strange at first glance since I use include guards, and all should be OK.Error 2 fatal error LNK1169: one or more multiply defined symbols found E:\test.exe
But they are intended to only avoid the problem of double inclusion.
The error occurs when the linker finds two matches that collide.
In our case there are two object files - mainfile.obj and testing.obj.
Each of them has TestNS::namespaceData. A solution is not to define the variable in the header file. It´s needed to be defined in the source file and only declared in the header fileby use extern directive
//file testing.h
namespace TestNS
{
extern int namespaceData;
};
//file testing.cpp
namespace TestNS
{
int namespaceData;
}; Now it works
Another way to get rid of the error is to declare the variable as constant
//file testing.h
namespace TestNS
{
const int namespaceData;
};
суббота, 29 мая 2010 г.
C++ и Static
Рассмотрим употребление ключевого static в языке С++. Думаю кому-то это сообщение будет полезно.
Рассмотрим следующие обасти видимости, имеющиеся в С++:
Local scope (область видимости - блок)
Function scope (область видимости - функция)
File scope (область видимости - файл)
Class scope (область видимости - класс/структура)
Начнем с самого простого.
Допустим в блоке находится локальная статическая переменная:
Аналогично дело обстоит с функциями:
В обоих приведенных примерах статическая переменная видна только в пределах блока, в котором она определена.
Нам ничто не мешает создать глобальную статическую переменную, которая будет видна повсюду в файле и только в файле, в котором она определена, и никаким extern`ом к ней из другого файла не обратиться:
Классы, как известно, могут содрежать данные и методы, данные и методы могут быть статическими. Статические члены в большинстве случаев ведут себя так же как и нестатические. Они так же подчиняются модификаторам доступа (public, private, protected). Они содержатся только в области видимости данного конкретного класса. Доступ к данным осуществляется посредством операторов '.' или '->'. Но, в отличие от нестатических данных, к статическим можно обращаться с помощью оператора '::', используя имя класса. И для этого совсем необязательно, чтобы объект данного класса был создан.
Статические данные разделяются между всеми экземплярами класса. Они действуют подобно глобальным данным для класса.
Статические данные класса находят множество применений. Допустим, подсчет количества экземпляров данного класса:
Однажды созданная статическая переменная существуе на протяжении всего времени работы программы. Все статические переменные разрушаются (вызываются их деструкторы)при возврате из main() во время вызова atexit(). Деструкторы статической переменной вызываются только если переменная была создана и инициализирована. Это означает, что статический локальный объект в области видимости функции не нуждается в разрушении, если эта функция не была вызвана. В данном примере использование локальной статической переменной в синглтоне избавляет от проблемы утечки ресурсов, поскольку объект разрушается при выходе из программы.
Опишем класс со статической переменной-членом:
Можно проводить инициализацию с помощью выражения (в данном случае, с помощью функции, возвращающей значение)
Статические переменные-члены не могут содержаться в локальных класса, а так же они не могут быть mutable.
Методы класса также могут быть статическими. В отличие от обычных методов класса, статические методы не имеют указателя this, поэтому статические методы не могут обращаться к нестатическим членам класса.
Статические методы чаще всего используются для манипуляций со статическими данными класса:
Статические методы не могут быть виртуальными. Не может присутствовать одновременно статический и нестатический методы с одинаковыми типами параметров. Так же статические методы не могут быть помечены модификаторами const, volatile и const volatile. Локальные классы не могут содержать статических членов
Всё...
Не совсем наверно всё. Остались моменты, которые я хотел бы обсудить с кем-либо, но ... не сейчас
Рассмотрим следующие обасти видимости, имеющиеся в С++:
Local scope (область видимости - блок)
Function scope (область видимости - функция)
File scope (область видимости - файл)
Class scope (область видимости - класс/структура)
Начнем с самого простого.
Допустим в блоке находится локальная статическая переменная:
while(true)
{
static int i=0;
i++;
}
В этом случае переменная i будет создана лишь раз, и значение ее будет храниться втчение времени выполнения программы, несмотря на выход за пределы области видимости данного блока.Аналогично дело обстоит с функциями:
void Foo()
{
static int i=0;
i++;
}
В данном случае значение переменной i будет сохраняться между вызовами Foo().В обоих приведенных примерах статическая переменная видна только в пределах блока, в котором она определена.
Нам ничто не мешает создать глобальную статическую переменную, которая будет видна повсюду в файле и только в файле, в котором она определена, и никаким extern`ом к ней из другого файла не обратиться:
// File: example.cpp // Global static local to this file: static int i;Рассмотрим классы.
Классы, как известно, могут содрежать данные и методы, данные и методы могут быть статическими. Статические члены в большинстве случаев ведут себя так же как и нестатические. Они так же подчиняются модификаторам доступа (public, private, protected). Они содержатся только в области видимости данного конкретного класса. Доступ к данным осуществляется посредством операторов '.' или '->'. Но, в отличие от нестатических данных, к статическим можно обращаться с помощью оператора '::', используя имя класса. И для этого совсем необязательно, чтобы объект данного класса был создан.
Статические данные разделяются между всеми экземплярами класса. Они действуют подобно глобальным данным для класса.
Статические данные класса находят множество применений. Допустим, подсчет количества экземпляров данного класса:
class A
{
static int i;
public:
A(){i++}
}
Паттерн одиночка(singleton) так же использует статическую переменную для того, чтобы гарантировать, что класс будет создан единожды при первом обращении.Однажды созданная статическая переменная существуе на протяжении всего времени работы программы. Все статические переменные разрушаются (вызываются их деструкторы)при возврате из main() во время вызова atexit(). Деструкторы статической переменной вызываются только если переменная была создана и инициализирована. Это означает, что статический локальный объект в области видимости функции не нуждается в разрушении, если эта функция не была вызвана. В данном примере использование локальной статической переменной в синглтоне избавляет от проблемы утечки ресурсов, поскольку объект разрушается при выходе из программы.
class Singleton
{
private:
Singleton(){}
public:
static Singleton& Instance()
{
static Singleton inst;
return inst;
}
};
Данная разновидность синглтона называется синглтон Мейерса (подробнее Александреску)Опишем класс со статической переменной-членом:
// File: example.h
class A
{
public:
A();
static int i;
};
Но объявление статической переменной внутри определения класса не является ее определением. Определение переменной должно присутствовать в области видимости того пространства имен, в котором заключено определение класса данной статической переменной. Поэтому код следует дополнить следующим образом:class A
{
public:
A();
static int i;
};
int A::i = 99;
Если не инициализировтаь значение статической переменной, то по умолчанию будет присвоено значение 0.Можно проводить инициализацию с помощью выражения (в данном случае, с помощью функции, возвращающей значение)
// File: example.cpp int A::i = GetValue();Допускается инициализация внутри определения класса для переменных интегрального типа: int, char, long, bool (ISO Standard, 9.4.2), а также типов из пространства std, таких как std::float_round_style и т.д.
Статические переменные-члены не могут содержаться в локальных класса, а так же они не могут быть mutable.
Методы класса также могут быть статическими. В отличие от обычных методов класса, статические методы не имеют указателя this, поэтому статические методы не могут обращаться к нестатическим членам класса.
Статические методы чаще всего используются для манипуляций со статическими данными класса:
// File: example.h
class A
{
public:
A();
static int GetValue () { return val; }
private:
static int val;
};
Статические методы также могут быть доступны посредством операторов '.' и '->' по имени объекта, либо по имени класса с помощью оператора '::'.Статические методы не могут быть виртуальными. Не может присутствовать одновременно статический и нестатический методы с одинаковыми типами параметров. Так же статические методы не могут быть помечены модификаторами const, volatile и const volatile. Локальные классы не могут содержать статических членов
Всё...
Не совсем наверно всё. Остались моменты, которые я хотел бы обсудить с кем-либо, но ... не сейчас
воскресенье, 23 мая 2010 г.
Qt и условная сборка qmake
Попытаюсь сэкономить кому-нибудь время,силы и нервы.
В одном из Qt проектов (.lib) нужно было сделать так, чтобы при сборке в режиме debug сгенерированная dll помещалась в дерикторию debug, а при сборке в режиме release - в директорию release.
Решение состояло в том, чтобы прописать в pro-файле следующее:
Дело было в том, что переменная CONFIG уже содержала в себе значения и debug, и release.
Результат выполнения message($$CONFIG) такой:
В одном из Qt проектов (.lib) нужно было сделать так, чтобы при сборке в режиме debug сгенерированная dll помещалась в дерикторию debug, а при сборке в режиме release - в директорию release.
Решение состояло в том, чтобы прописать в pro-файле следующее:
CONFIG += designer plugin debug
TEMPLATE = lib
TARGET = $$qtLibraryTarget($$TARGET)
.....................
debug {
DLLDESTDIR += ../MyApp/debug
message(1)
}
release {
DLLDESTDIR += ../MyApp/release
message(2)
}
Но почему-то сгенерированная dll помещалась всегда в обе папке, независимо от режима сборки. И на консоль выдавались оба сообщения - message(1) и message(2)TEMPLATE = lib
TARGET = $$qtLibraryTarget($$TARGET)
.....................
debug {
DLLDESTDIR += ../MyApp/debug
message(1)
}
release {
DLLDESTDIR += ../MyApp/release
message(2)
}
Дело было в том, что переменная CONFIG уже содержала в себе значения и debug, и release.
Результат выполнения message($$CONFIG) такой:
Project MESSAGE: lex yacc warn_on debug uic resources rtti_off exceptions_off stl_off incremental_off thread_off windows release ReleaseBuild Release build_pass qt warn_on release incremental flat link_prl precompile_header autogen_precompile_source copy_dir_files debug_and_release debug_and_release_target embed_manifest_dll embed_manifest_exe debug shared stl exceptions rtti mmx 3dnow sse sse2 def_files release ReleaseBuild Release build_pass designer plugin debug
Решение состоит в использовании функции CONFIG и выглядит следующим образом:CONFIG( debug, debug|release ) {
DLLDESTDIR += ../MyApp/debug
message(1)
} else {
DLLDESTDIR += ../MyApp/release
message(2)
}
Теперь будет выполняться только единственная ветвь в зависимости от режима сборки.
DLLDESTDIR += ../MyApp/debug
message(1)
} else {
DLLDESTDIR += ../MyApp/release
message(2)
}
Подписаться на:
Сообщения
(
Atom
)


