[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ą.
Bitowa reprezentacja liczb całkowitych
Kilka przydatnych funkcji w różnych językach których wspólną cechą jest pełniona zadanie – wszystkie tworzą binarną reprezentację liczb całkowitych. Zanim przejdę do konkretów (tj kodu źródłowogo) trochę teorii ;P. Sprawdzenie, czy n-ty bit liczby całkowitej w reprezentacji dwójkowej x jest zapalony można zasadniczo wykonać na trzy sposoby – jeden dobry i dwa teoretycznie dobre. Dwie teoretycznie dobre to
- sprawdzenie czy zapalony jest n-ty bit przez operację koniunkcji bitowej z maską
1 << nx & (1 << n)
- przesunięcie bitowe
xobitsof(x) - nmiejsc w lewo i sprawdzenie, czy zapalony jest najstarszy bit(x << n ) & 1 << (bitsof(x) - 1)
gdzie bitsof(x) ≡ sizeof(type(x)) * BITS_PER_BYTE
Powyższe metody jednak nie są uniwersalne. W większości przypadków typów i języków zadziałają poprawnie. Ale wyobraźmy sobie taką sytuację w języku C++
#define IS_BIT_SET(x, n) ((x) & (1 << (n))) long long x = 1; bool is32ndBitSet = IS_BIT_SET(x,32); // TRUE, absurd !
Przyczyna błędu jest oczywista, i na całą sytuację prawdopodobnie zwróci nam uwagę kompilator odpowiednim ostrzeżeniem ( w przypadku MSVC będzie to C4334 ). Makro to można naprawić
#define IS_BIT_SET(x, n) ((x) & (1LL << (n)))
ale można też spróbować czegoś innego
- przesunięcie bitowe
xo n miejsc w prawo i sprawdzenie, czy najmłodzy bit jest zapalony(x >> n) & 0x1
C++
#include <string>
#define IS_BIT_SET(x, n) ((x) & (1LL << (n)))
#define SET_BIT(x, n) ((x) |= (1LL << (n)))
#define UNSET_BIT(x, n) ((x) |= ~(1LL << (n)))
template<typename T>
std::string toBinStr(T x) {
std::string s;
int bits = sizeof(T) * 8;
for(int b=bits-1; b>=0; --b) {
s+=IS_BIT_SET(x,b)?'1':'0';
}
return s;
}
Python
def to_bin_str(x):
import types
if type(x) is types.IntType: bits = 32;
else: raise "Argument must by int type!"
mask = 1 << bits - 1
return ''.join('0' if x<<shift & mask == 0 else '1' for shift in range(0,bits))
C#
class BinUtil
{
public static string toBinStr(int x)
{
int bits = 32;
StringBuilder sb = new StringBuilder(bits);
for(int i=bits - 1; i>=0 ; i--)
{
sb.Append( (x >> i & 0x1 )== 0? "0":"1");
}
return sb.ToString();
}
public static string toBinStr(uint x)
{
int bits = 32;
StringBuilder sb = new StringBuilder(bits);
for(int i=bits - 1; i>=0 ; i--) {
sb.Append( (x >> i & 0x1 )== 0? "0":"1");
}
return sb.ToString();
}
public static string toBinStr(long x)
{
int bits = 64;
StringBuilder sb = new StringBuilder(bits);
for(int i=bits - 1; i>=0 ; i--) {
sb.Append( (x >> i & 0x1 )== 0? "0":"1");
}
return sb.ToString();
}
public static string toBinStr(ulong x)
{
int bits = 64;
StringBuilder sb = new StringBuilder(bits);
for(int i=bits - 1; i>=0 ; i--) {
sb.Append( (x >> i & 0x1 )== 0? "0":"1");
}
return sb.ToString();
}
public static string toBinStr(byte b)
{
int bits = 8;
StringBuilder sb = new StringBuilder(bits);
for(int i=bits - 1; i>=0 ; i--){
sb.Append( (b >> i & 0x1 )== 0? "0":"1");
}
return sb.ToString();
}
}
PS
Tytuł posta może być trochę mylący co do jego treści, ale przecież każdy wie dlaczego int i = -1 ≡ 11111111111111111111111111111111b, prawda?
PS PS
Chyba, że procesor nie stosuje arytmetyki uzupełnień do dwójki. Wtedy oczywiście powyższe stwierdzenie nie jest prawdziwe.
[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>
Python i przetwarzanie danych z www
Najpierw trzeba oczywiście stronę www pobrać
from urllib2 import urlopen
def get_www(url):
"Get web page html"
try:
content = urlopen(url).read()
return content
except:
return ""
a potem można już zrobić z nią co się chce, np. znaleźć wszystkie odnośniki. Można do tego wykorzystać klasę sgmllib.SGMLParser
from sgmllib import SGMLParser
class URLParser(SGMLParser):
def reset(self):
SGMLParser.reset(self)
self.urls = []
def start_a(self, attrs):
href = [v for k, v in attrs if k=='href']
if href:
self.urls.extend(href)
html = get_www("http://impact.arc.nasa.gov/")
parser = URLParser()
parser.feed(html)
print parser.urls
Jeśli potrzebujemy czegoś więcej to warto skorzystać z PyXML
MiKTeX + Uft8
Dzięki projektowi MiKTeX wykorzystanie LaTeX’a w Windows jest bardzo proste. Jeśli jednak ktoś [tak jak ja] pracuje nad tym samym projektem pod różnymi OS’ami może miec kłopoty. Wiekszośc poradników twierdzi bowiem, ze pod Linuxem należy korzystac z kodowania a Latin-2 (ISO 8859-2) a pod Windows cp1250 (Win Latin 2). Dlaczego nie użyc UTF-8? Poniżej przestawiam szablon dokumentu którego z powodzeniem używam
\documentclass[a4paper]{article}
\usepackage[utf8]{inputenc}
\usepackage[OT4]{polski}
\usepackage{bera}
\begin{document}
\end{document}
Trzeba tylko uważac, żeby plik źródłowy nie zawierał BOM (byte order mark).
[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);
[Java] static import
Jedną z nowych rzeczy dodanych w Javie 1.5 jest statyczny import. Umożliwia on dostęp do statycznym metod i pól klasy bez odwoływania się do jej nazwy. Jest on szczególnie przydatny w przypadku klas użytkowych takich jak np java.lang.Math. Możemy wówczas napisać
import static java.lang.Math.*; // double r = cos(PI * theta);
zamiast
double r = Math.cos(Math.PI * theta);
Nie jest to może rewolucja, ale cecha w pewnych sytuacjach bardzo przydatna.
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 .