Архив рубрики: C Plus Plus

Обёртка для вызова функций из dll при динамическом связывании

Как-то раз мне пришлось загружать очень много функций из dll. После десятого вызова GetProcAddress и добавление переменной в класс (к тому же функции вызывались не из одной библиотеки, что приводило ещё к вызовам LoadLibrary и FreeLibrary) я решил автоматизировать этот процесс раз и навсегда.

#ifndef DYNAMIC_LIB_PROC
#define DYNAMIC_LIB_PROC

class CDynamicLibProcException
{
// Здесь какая-то начинка исключения.
};

struct CUnloadLibOnExeption
{
void operator () (HMODULE hlib)
{
if(hlib)
::FreeLibrary(hlib);
}
};

struct CNotUnloadLibOnExeption { void operator () (HMODULE) { } };

template<typename ProcT, typename ExceptionActionT = CUnloadLibOnExeption>
class CDynamicLibProc
{
public:
CDynamicLibProc(LPCTSTR libname, LPCSTR procname,
bool unload_lib_on_destroy = true) :
m_hlib(::LoadLibrary(libname)),
m_unload_lib_on_destroy(unload_lib_on_destroy)
{
LoadProc(procname);
}

CDynamicLibProc(const HMODULE hlib, LPCSTR procname,
bool unload_lib_on_destroy = false) :
m_hlib(hlib),
m_unload_lib_on_destroy(unload_lib_on_destroy)
{
LoadProc(procname);
}

~CDynamicLibProc()
{
if(m_unload_lib_on_destroy && m_hlib)
::FreeLibrary(m_hlib);
}

HMODULE GetLibHandle() const
{
return m_hlib;
}

public:
ProcT Execute;

private:

void LoadProc(LPCSTR procname)
{
if(!m_hlib)
throw CDynamicLibProcException(); // Тут Вы дополняете своё исключение.
Execute = (ProcT)::GetProcAddress(m_hlib, procname);
if(!Execute)
{
ExceptionActionT ea;
ea(m_hlib);
throw CDynamicLibProcException(); // Тут Вы дополняете своё исключение.
}
}

private:
HMODULE m_hlib;
bool m_unload_lib_on_destroy;
};

#endif // #ifndef DYNAMIC_LIB_PROC

Ну, а использовать этот класс можно так
#include <windows.h>
#include <tchar.h>
#include "DynamicLibProc.h"

int main()
{
CDynamicLibProc<DWORD (WINAPI *)(HWND, LPCWSTR, DWORD, DWORD)>
message_box(_T("user32.dll"), "MessageBoxW");
message_box.Execute(NULL, L"Hello from MessageBox", NULL, 0);
return 0;
}

"Умные" указатели с подсчётом ссылок

Язык C++ был разработан с учётом специфики системного программирования, поэтому не имеет встроенного сборщика мусора. Но порой требуется передать куда-то указатель и не заботиться о его дальнейшей судьбе. Для этих целей существует такое понятие, как "Умные указатели".
Концепция заключается в том, что Вы пишите класс-обёртку для указателей и определяете в нём необходимые операторы. В деструкторе этого класса следует высвободить память, которую занимает указатель. Для того, что бы не удалить указатель раньше времени, принято считать количество ссылок на объект. Для этого объявляем счётчик-переменную в куче и в операторе присвоения и копирующем конструкторе инкрементируем его, а в деструкторе - декрементируем. Как только счётчик в деструкторе достигает нуля - высвобождаем всю занятую память. Следующий пример демонстрирует концепцию создания класса "умного" указателя.

#ifndef SMARTPTR_H
#define SMARTPTR_H

namespace SmartPtr {


template<typename TypeT>
class CSmartPtr
{
public:
explicit CSmartPtr(TypeT * ptr = NULL)
{
mp_pointer = ptr;
mp_link_counter = new int;
*mp_link_counter = 1;
}

CSmartPtr(const CSmartPtr<TypeT> & rhs)
{
mp_pointer = rhs.mp_pointer;
mp_link_counter = rhs.mp_link_counter;
(*mp_link_counter)++;
}

~CSmartPtr()
{
Release();
}

CSmartPtr<TypeT> & operator = (const CSmartPtr & rhs)
{
if(&rhs != this)
{
Release();
mp_pointer = rhs.mp_pointer;
mp_link_counter = rhs.mp_link_counter;
(*mp_link_counter)++;
}
return *this;
}

TypeT * operator -> ()
{
return mp_pointer;
}

TypeT operator * ()
{
return *mp_pointer;
}

TypeT * GetPointer()
{
return mp_pointer;
}


private:
void Release()
{
if(! --(*mp_link_counter))
{
delete mp_link_counter;
mp_link_counter = NULL;
if(mp_pointer)
{
delete mp_pointer;
mp_pointer = NULL;
}
}
}

private:
TypeT * mp_pointer;
int * mp_link_counter;
};

} // namespace SmartPtr

#endif // SMARTPTR_H

Приведу небольшой пример, использующий этот класс.
#include <iostream>
#include "SmartPtr.h"

class CTest
{
public:
~CTest()
{
std::cout << "destructorn";
}

void print() const
{
std::cout << "i'm testn";
}
};


void funct(SmartPtr::CSmartPtr<CTest> test)
{
test->print();
}

int main()
{
SmartPtr::CSmartPtr<CTest> test(new CTest);
funct(test);
return 0;
}

Вывод программы, как и ожидалось, будет таким
i'm test
destructor
В некоторых реализациях я встречал определение оператора приведения типа. Так как приведение типов в C++ не приветствуется, то и я не стал определять этот оператор. В случае крайней нужды можно воспользоваться методом GetPointer.