вторник, 16 апреля 2013 г.

Как определить, есть ли файл на диске (Windows)

Для этого надо сравнить результат функции GetFileAttributes с -1:

GetFileAttributes(path) != DWORD(-1);

У функции GetFileAttributes есть две версии: для однобайтных символов (GetFileAttributesA) и для Unicode (GetFileAttributesW).

Приведение типов в C++

Немного о приведении типов.

в С++ есть несколько вариантов приведения типов:


  • const_cast,
  • dynamic_cast,
  • static_cast,
  • reinterpret_cast,
  • C-style cast.
const_cast используется для того, чтобы снять константность с объекта.

dynamic_cast используется для приведения типов в иерархии наследования.

static_cast используется для приведения одного типа к другому. При этом для встроенных типов используются встроенные правила приведения типов, а для остальных - правила, определенные программистом.

reinterpret_cast используется для низкоуровневого преобразования указателей. Компилятор считает, что программист лучше знает, как трактовать данную область памяти и тихо подчиняется. Следует применять с большой осторожностью.

C-style cast лучше не использовать в C++, потому что его трудно обнаружить в коде.
Приведение в стиле C делает static_cast, если он не подходит, делает reinterpret_cast, после этого, если надо, делает const_cast. Единственный вариант, которому нет аналога в C++ - это приведение к void.

Что делать, если в Windows 7 не работает центр обновлений и выдает ошибку 0xc8000222

Такое бывает, например, когда вы восстановили систему с образа восстановления на другую конфигурацию компьютера. У меня такое было, когда у меня посыпался винчестер на ноутбуке, и я восстановил систему уже на новый винчестер другого размера.

Для исправления требуется сделать следующие шаги:

1. Нажмите кнопку Пуск, Выполнить, введите команду: CMD и нажмите Enter. Выполните следующую команду в открывшемся окне.
net stop WuAuServ
2. Переименуйте папку %windir%\SoftwareDistribution в %windir%\SDold.
4. Нажмите кнопку Пуск, Выполнить, введите команду: CMD и нажмите Enter. Выполните следующую команду в открывшемся окне.
net start WuAuServ


Специализация метода шаблонного класса

Сегодня рассмотрим специализацию шаблонов.

Создадим класс шаблонный A.

#include <string>
#include <iostream>

template <class T>
class A
{
private:
std::string f;
public:
void method();
};

template<class T>
void A<T>::method()
{
   f = "some type";
   std::cout << f << std::endl;
}


Теперь создадим специализацию для int:

template<>
class A<int>
{
private:
  std::string f;
public:
  void method();
};


Если мы омределим method следующим образом:

template <>
void A<int>::method()
{
  f = "int";
  std::cout << f << std::endl;
}
,
то получим ошибку о том, что метод нельзя спицифицировать дважды. В Visual Studio это ошибка №C2910. MSDN говорит, что требуется убрать template <> . Однако в полученном коде:
void A<int>::method()
{
  f = "int";
  std::cout << f << std::endl;
}

возникает ошибка линковки - функция-член method определена дважды в объектном файле.

Работающий вариант:
inline void A<int>::method()
{
  f = "int";
  std::cout << f << std::endl;
}


среда, 10 апреля 2013 г.

Shared library

Сегодня будем писать кросс-платформенную динамическую библиотеку (DLL).

Заведем заголовочный файл, в котором будут макросы для экспортирования функций в библиотеку в зависимости от платформы.

Файл export.h:

#ifndef _EXPORT_H
#define _EXPORT_H


#if defined (_MSC_VER)
#ifdef __cplusplus
#define LIB_EXPORT extern "C" __declspec(dllexport)
#else
#define LIB_EXPORT __declspec(dllexport)
#endif
#elif defined (__GNUC__)
#ifdef __cplusplus
#define LIB_EXPORT extern "C" __attribute__((visibility("default")))
#else
#define LIB_EXPORT __attribute__((visibility("default")))
#endif
#else
#warning Unknown dynamic link semantics
#ifdef __cplusplus
#define LIB_EXPORT extern "C" 
#else
#define LIB_EXPORT 
#endif
#endif


#endif


Теперь создадим саму библиотеку. Покажем, как делать экспорт функций и классов.

Файл export_functions.h:

#ifndef EXPORT_FUNCTIONS_H
#define EXPORT_FUNCTIONS_H


#include "export.h"


LIB_EXPORT int export_function(int x);


class EClass
{
public:
virtual void func() =0;
};


class EClassImpl : public EClass
{
public:
virtual void func();
};


LIB_EXPORT EClass *GetEClass();
LIB_EXPORT void DeleteEClass(EClass *a);


#endif

Файл export_functions.cpp:
#include <iostream>

#include "export_functions.h"

using namespace std;

int export_function(int x)
{
cout << "DLL function! parameter: " << x << endl;
return x;
}

EClass *GetEClass()
{
return new EClassImpl;
}

void DeleteEClass(EClass *a)
{
delete a;
}

void EClassImpl::func()
{
cout << "Class function from DLL!" << endl;
}

Положим все эти файлы в папку SharedLibrary.


Для сборки под Linux при помощи GCC можно использовать следующий Makefile:
libshared:
g++ -fPIC -Wall -g -c export_functions.cpp
g++ -shared -Wl,-soname, -o libshared.so export_functions.o -lc


clean: 
rm -f *.o libshared.so 

Файл libshared.so требуется либо положить в стандартное место для библиотек, либо перед использованием изменять переменную LD_LIBRARY_PATH.

Для сборки в Visual Studio под Windows требуется создать проект типа Win32 Console application и указать, что это DLL.

Теперь создадим пользователя этой библиотеки. Первый вариант: У нас есть заголовочный файл и мы будем линковаться с ней при сборке.

Файл StaticUser.cpp:

#include <iostream>


#include "../SharedLibrary/export_functions.h"


int main(int argc, char **argv)
{
export_function(5);


EClass *a = GetEClass();
a->func();
DeleteEClass(a);
return 0;
}

Makefile для сборки под Linux:
StaticUser: 
g++ -Wall -g -c StaticUser.cpp 
g++ -g -o StaticUser StaticUser.o -L../SharedLibrary -lshared 


Для сборки в Visual Studio под Windows в зависимостях проект (Project dependencies) требуется поставить проект SharedLibrary.

Второй вариант: У нас нет заголовочного файла, и мы будем загружать библиотеку самостоятельно.

В Windows загрузка DLL осуществляется при помощи функции LoadLibrary. Если загрузка DLL произошла удачно, возвращается переменная типа HINSTANCE (В VisualStudio этот тип определен как void *). В противном случае возвращается 0. Чтобы понять, какая произошла ошибка, вызываем функцию GetLastError. Коды ошибок в Windows можно посмотреть тут.

Пользователь библиотеки будет состоять из одного файла LibraryUser.cpp:

#include <iostream>
#include <Windows.h>


using namespace std;


typedef int (*export_function_t) (int);
export_function_t export_function;


int main(int argc, char **argv)
{
DWORD err;
LPCSTR path = (LPCSTR)"SharedLibrary.dll";


if (GetFileAttributesA(path) == -1) //file does not exist.
{
cout << "Library not found ):" << endl;
}


HINSTANCE hDLL = LoadLibraryA(path);


if (hDLL)
{
cout << "Library loaded!" << endl;


export_function = reinterpret_cast<export_function_t> (GetProcAddress(hDLL, "export_function"));
if (!export_function)
{
cout << "Function not loaded ):" << endl;
return 1;
}


cout << "Function export_function loaded!" << endl;
int x = export_function(5);

cout << "Function returned " << x << endl;
}
else
{
err = GetLastError();
cout << "Error in load library: " << err << endl;
}
return 0;
}

Под Linux загрузка библиотеки осуществляется функцией dlopen.
Линуксовый пользователь библиотеки будет состоять из файла lu.cpp:

#include <iostream>
#include <dlfcn.h>
#include <link.h>


#include "../SharedLibrary/export_functions.h"


using namespace std;


int main(int argc, char ** argv)
{
void *handle = dlopen("/lib/libshared.so", RTLD_LAZY);


if (!handle)
{
cerr << "Couldn't load library ):" << endl;
return 1;
}


EClass * (*GetEClass)();
void * (DeleteEClass)(EClass *);


GetEClass = (EClass* (*)())dlsym(handle, "GetEClass");


EClass *a = (EClass *) GetEClass();
a->func();


return 0;
}

Собирается файл строкой g++ -o lu -ldl lu.cpp. Ключ -ldl подключает библиотеку, в которой находятся dlopen и другие функции для работы с динамическими библиотеками.