Archiwum kategorii ‘Cpp’
[C++] Statyczna metoda klasy jako punkt wejścia aplikacji w C++
W tym wpisie chciałem napisać o pewnej właściwości linkera MS – punktem wejścia aplikacji może być dowolna funkcja o odpowiedniej sygnaturze – w tym także statyczna metoda klasy! W przypadku aplikacji okienkowej i wykorzystaniu Unicode odpowiednia sygnatura sygnatura wygląda tak
int __stdcall EntryPt(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
WCHAR* lpCmdLine,
int nCmdShow)
Wystarczy poinformowac o tym linker. W przypadku produktu Microsoftu
link ${OBJS} /SUBSYSTEM:WINDOWS /ENTRY:EntryPt ${LIBS}
Jak już wspomniałem punktem wejścia może być statyczna metoda klasy
struct WinApp {
static int __stdcall wWinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
WCHAR* lpCmdLine,
int nCmdShow);
}
Oto mały przykład okienkowej aplikacji Win32 której punktem wejścia jest statyczna metoda metoda klasy
#define STRICT
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
/**
cl -c winapp.cpp
link winapp.obj /SUBSYSTEM:WINDOWS /ENTRY:WinApp::wWinMain kernel32.lib user32.lib gdi32.lib
**/
struct WinApp {
static int __stdcall wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, WCHAR* lpCmdLine, int nCmdShow);
static LRESULT __stdcall WndProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
};
int WINAPI WinApp::wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, WCHAR* lpCmdLine, int nCmdShow)
{
WNDCLASSEX wc = {
sizeof(WNDCLASSEX), CS_CLASSDC, WinApp::WndProc, 0L, 0L,
GetModuleHandle(NULL), NULL,
LoadCursor( NULL, IDC_ARROW ),
( HBRUSH )( COLOR_WINDOW ), NULL,
"WinApp", NULL };
RegisterClassEx( &wc );
HWND hWnd = CreateWindow(
"WinApp",
"«Jarosław Przygódzki» Statyczna metoda klasy jako punkt wejścia aplikacji w C++",
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 600, 400,
GetDesktopWindow(), NULL, wc.hInstance, NULL);
ShowWindow(hWnd, SW_SHOW);
MSG msg = {0};
while( GetMessage(&msg, NULL, 0, 0 ))
{
TranslateMessage( &msg );
DispatchMessage( &msg );
}
return 0;
}
LRESULT __stdcall WinApp::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch(uMsg) {
case WM_CLOSE:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
return 0L;
}
Proces kompilacji i linkowania nie jest skomplikowany
cl -c winapp.cpp link winapp.obj /SUBSYSTEM:WINDOWS /ENTRY:WinApp::wWinMain kernel32.lib user32.lib
PS
CALLBACK i WINAPI to właśnie aliasy na konwencję wywołania __stdcall
// WinDef.h #define CALLBACK __stdcall #define WINAPI __stdcall
Deklaracje mogą więc wyglądać następująco
struct WinApp {
static int WINAPI wWinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
WCHAR* lpCmdLine,
int nCmdShow);
static LRESULT CALLBACK WndProc(
HWND hWnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam)
}
I najczęściej tak właśnie wyglądają.
[C++] Uniwersalny ekstraktor i inserter dla strumieni binarnych
#include <istream>
#include <ostream>
template<class T>
std::istream& read(std::istream& is, T& t) {
is.read((char*)&t, sizeof(T));
return is;
}
template<class T>
std::ostream& write(std::ostream& os, const T& t) {
os.write((char*)&t, sizeof(T));
return os;
}
template<class T>
std::ostream& operator << (std::ostream& os, const T& t) {
return write<T>(os,t);
}
template<class T>
std::istream& operator >> (std::istream& is, T& t) {
return read<T>(is,t);
}
I mały przykład
#include <cstring>
#include <fstream>
struct Superheroine {
Superheroine(const char* name_ = "", const char* publisher_ = "") {
strcpy(name, name_);
strcpy(publisher, publisher_);
}
char name[32];
char publisher[32];
};
//...
std::ofstream ofs;
ofs.open("data.bin", std::ios::out | std::ios::binary | std::ios::trunc);
Superheroine
silverFox("Silver Fox","Marvel Comics"),
natashaIrons("Natasha Irons", "DC Comics");
write<Superheroine>(ofs, silverFox); // write<T>
ofs << natashaIrons; // operator <<
ofs.close();
ifs.open("data.bin", std::ios::in | std::ios::binary);
Superheroine heroine;
ifs >> heroine; // operator >>
// ...
read<Superheroine >(ifs,heroine); // read<T>
[C++] Użycie freopen do przekierowania standardowych strumieni
Za pomocą funkcji
#include <cstdio> FILE *freopen(const char *filename, const char *mode, FILE *stream);
możemy dokonac przekierowania standardowych strumieni (
stdin, stdout, stderr), co w pewnych sytuacjach może byc bardzo pomocne.
freopen("stdout.txt", "w", stdout);
freopen("stderr.txt", "w", stderr);
Właściwości w C++
No coż, w standardzie ich po prostu nie ma. Niektóre kompilatory, jak np mój ulubiony MSVC oferują je za pomocą rozszerzeń, inne nie oferują ich wcale. Wspomniany już kompilator MS wspiera je jednak znakomicie.
Składnia jest prosta
__declspec (property (get=nameOfGetFunction, put=nameOfSetFunction)) type propertyName;
np
class Renderer {
private:
ID3D10Device* m_pDevice;
ID3D10Device& GetDevice(){ return *m_pDevice;}
public:
__declspec (property (get=GetDevice)) ID3D10Device& Device;
}
Szablony? Oczywiście.
template<class T>
class vec3 {
public:
inline T get_x() const { return m_v[0]; }
inline T get_y() const { return m_v[1]; }
inline T get_z() const { return m_v[2]; }
inline void set_x(T value) { m_v[0] = value; }
inline void set_y(T value) { m_v[1] = value; }
inline void set_z(T value) { m_v[2] = value; }
__declspec(property(get=get_x,put=set_x)) T x ;
__declspec(property(get=get_y,put=set_y)) T y ;
__declspec(property(get=get_z,put=set_z)) T z ;
private:
T m_v[3];
}
Właściwości wirtualne?
struct Shape {
float computeArea()=0;
__declspec(property(get=computeArea)) float Area;
}
struct Rect : Shape {
Rect(float w, float h): m_w(w),m_h(h) {}
float computeArea() {
return m_w * m_h;
}
private:
float m_w, m_h;
}
struct Circle : Shape {
Circle(float r) : m_radius(r) {}
float computArea() {
return M_PI * m_radius * m_radius;
}
private:
float m_radius;
}
//...
Shape& s = *new Circle(1.0f);
float area = s.Area;
Oczywiście, świat nie jest idealny, więc i rozszerzenie kompilatora __declspec(property) nie jest, m.in
właściwości nie mogą być statyczne a i z szablonami nie zawsze wszystko działa.
Jeśli ktoś potrzebuje rozwiązań nie ograniczonych do jednego kompilatora, rozwiazanie stanowi biblioteka STLSoft Properties .