きままにブログ

プログラミングを主とした私のメモ帳です。寂しいのでコメントください笑

リファレンスクラス

定数を返すには巨大過ぎるデータを扱う場合できれば参照やポインタで返したいです。ところが、ポインタを直接渡すと、そのポインタが変更されるような場合困ります。

そもそも参照やポインタで返すべきではないのですが、場合によっては参照で受け取りたいような時があります。まず、=演算子や*演算子は通常参照を返します。ただし返すのは*thisですね。こうすることで戻り値もその対象とナブオブジェクト自信と同等に扱えるようになって便利なのです。

ポインタで返すことは通常有り得ません。返すならスマートポインタを利用します。ところが、ポインタの中身が変更されうるようなケースでは「参照」を返せません。即ちスマポでも困るような場合です。

そういうときは直接index等を返しますが、汎用性に掛けます。そこでRferenceクラスを作ってそのReferenceを返すことで後でこれを用いてアクセスできるというわけです。

#include <cstdio>
#include <vector>

template <typename Friend, typename IDType>
class Reference {
	friend typename Friend;
public:
	Reference(const Reference& it) : id(it.id) { }
	Reference& operator=(const Reference& it) { id = it.id; }
private:
	Reference(IDType id) : id(id) {}
	operator IDType() { return id; }
	IDType id;
};

class C {
	using ref = Reference < C, int > ;
public:
	ref create(int x) {
		v.push_back(x);
		size_t index = v.size() - 1;
		return ref(index);
	}
	int get(ref id) {
		return v[id];
	}
private:
	std::vector<int> v;
};

int main() {
	C c;
	auto it = c.create(10);
	auto it2 = c.create(20);
	printf("%d, %d", c.get(it), c.get(it2)); // 10, 20
}

コンテナ要素に並列アクセスで初期化したいとき

std::transformのbinary operationの使い方:

array<int, 3> a1 = { 1, 2, 3 };
array<int, 3> a2 = { 4, 5, 6 };
array<int, 3> a3;

std::transform(a1.begin(), a1.end(), a2.begin(), a3.begin(),
	[](const int a, const int b) {
	return a * b;
});

for(const auto it : a3) {
	printf("%d, ", it);
}

a1, a2を与えて(|a1|<|a2|とする)、a3(|a1|<|a3|とする)を2つの要素を以て初期化する。

そんな感じで並列に処理するfor_eachを作った。

#include <cstdio>
#include <array>
#include <algorithm>
using std::array;

template <typename S, typename T, typename Func>
void para_for_each(S& s, T& t, Func f) {
	for(auto it_s = s.begin(), it_t = t.begin(); it_s != s.end(); ++it_s, ++it_t) {
		f(*it_s, *it_t);
	}
}

int main() {
	array<int, 3> a1;
	array<int, 3> a2;
	
	para_for_each(a1, a2,
		[](int& a, int& b) {
		static int i = 0;
		a = i;
		b = i * 2;
		++i;
	});

	const auto view = [](const int it) { printf("%d, ", it); };

	printf("\na1 = ");
	std::for_each(a1.begin(), a1.end(), view);
	printf("\na2 = ");
	std::for_each(a2.begin(), a2.end(), view);

	getchar();
	return 0;
}

vectorとarrayをつかう

どちらも当然引数に渡すとコピーされる。(一応確認)

void func(vector<int> v) {
	v[0] = 3;
}

void func(array<int, 3> v) {
	v[0] = 3;
}

int main() {
	vector<int> v1 = { 1, 2, 3 };
	array<int, 3> v2 = { 1, 2, 3 };
	
	func(v1);
	func(v2);

	printf("%d, %d", v1[0], v2[0]);
}

当たり前だけど…。変更したければ参照を用いる。

array<int, 3> v;

const auto l = [](array<int, 3>& it, size_t n) {
	for(auto& it : it) {
		it = n;
	}
};

l(v, 10);

は当然10で埋められる。

arrayで確保するはいいがコンストラクタの引数を与えられないので、改めて要素を確保して0で初期化する例 :

array<vector<int>, 3> v;

const auto l = [](array<vector<int>, 3>& it, size_t size) {
	for(auto& it : it) {
		it = vector<int>(size);
		std::fill(it.begin(), it.end(), 0);
	}
};

l(v, 5);

v[0][0] = 0;

ドラッグ&ドロップ

にて謎が解決しました。ありがとうございます。〔私は質問者ではないのですが〕

以下のようにマウスの移動および移動中のフラグをMOUSEEVENTF_LEFTDOWNとすることでドラッグ&ドロップが再現できました。ただし、移動のdx, dyは絶対で移動したい場合、MOUSEEVENTF_ABSOLUTEフラグを立てて係数65535 / GetSystemMetrics(SM_CXSCREEN)を掛ける必要があります。最小の例なのでここでは省略してあります。

INPUT inputs[4];

inputs[0].type = INPUT_MOUSE;
inputs[0].mi.dwFlags = MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE;
inputs[0].mi.dx = (DWORD)53 * 65535 / GetSystemMetrics(SM_CXSCREEN);
inputs[0].mi.dy = (DWORD)57 * 65535 / GetSystemMetrics(SM_CXSCREEN);

inputs[1].type = INPUT_MOUSE;
inputs[1].mi.dwFlags = MOUSEEVENTF_LEFTDOWN;

inputs[2].type = INPUT_MOUSE;
inputs[2].mi.dwFlags = MOUSEEVENTF_MOVE | MOUSEEVENTF_LEFTDOWN;
inputs[2].mi.dx = 100;
inputs[2].mi.dy = 100;

inputs[3].type = INPUT_MOUSE;
inputs[3].mi.dwFlags = MOUSEEVENTF_LEFTUP;

メンバ変数のm

この日記ではメンバ変数にm_をつけて、ローカル変数と区別する意味、コード中の意味の違いについて述べられています。私はメンバ変数にm_など付けるのは冗長だと考えています。変数を使うとき、それがどんな変数か把握しながら書かないのでしょうか?

class C {
public:
	C() = default;

	void doSomething() {
		x = 10;
	}

	void func() {
		x = 1;
		doSomething();
		const int y = x; // xは1でないかも; 当たり前
	}

	void func2() {
		const int x = 1;
		doSomething();
		const int y = x; // xは1である
	}

private:
	int x;
};

Windowを表示するだけのクラス

f:id:staryoshi:20150127191153p:plain

ウィンドウを複数作れるように、基本となるクラスを作ってみた。まあ、ウィンドウをたくさん作ることもあまりないし、細かいことをしようと思うとどうせプロシージャをいじる必要が出てくるのであまり使えないですが、次のように継承することで簡単にウィンドウを生成できます。ただし、ウィンドウクラスは自分で登録する必要があり、コンストラクタを呼び出す前に作らないといけません;

テストケース

int WINAPI WinMain(HINSTANCE h_instance, HINSTANCE h_prev_instance, LPSTR command_line, int command_show) {
	try {
		EditWindow::Register();
		EditWindow ew;
		ew.show();
	}

	catch(WindowException& e) {
		return -1;
	}

	return Application::Run();
}

定義

  • edit_window.h
#pragma once
#include "window.h"

class EditWindow : public Window<EditWindow> {
public:
	EditWindow();
	void onLButtonUp() override;
	static void Register();
};
  • edit_window.cpp
#include "edit_window.h"

EditWindow::EditWindow() {
	create("window name", 100, 100, 100, 100);
}

void EditWindow::onLButtonUp() {
	MessageBox(h_wnd, "TEST", "TEST", MB_OK);
}

void EditWindow::Register() {
	WNDCLASS wndc;

	wndc.style = CS_HREDRAW | CS_VREDRAW;
	wndc.lpfnWndProc = WindowProc;
	wndc.cbClsExtra = wndc.cbWndExtra = 0;
	wndc.hInstance = GetModuleHandle(NULL);
	wndc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
	wndc.hCursor = LoadCursor(NULL, IDC_ARROW);
	wndc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	wndc.lpszMenuName = NULL;
	class_name = wndc.lpszClassName = "MainWindow";

	if(!RegisterClass(&wndc)) {
		throw WindowException::RegisterException();
	}
}

実装

  • window.h
#pragma once
#include <Windows.h>
#include <exception>

template <typename DerivedWindow>
class Window {
public:
	Window() = default;
	
	void create(const char* title, int x, int y, int width, int height) {
		CreateWindow(
			class_name, title,
			WS_OVERLAPPEDWINDOW,
			x, y, width, height, NULL, NULL,
			GetModuleHandle(NULL), this);

		// ここに来るのはWM_CREATEメッセージの処理の後
		if(h_wnd == NULL) {
			throw std::exception();
		}
	}
	void show() {
		ShowWindow(h_wnd, SW_SHOW);
	}

protected:
	virtual void onCreate() { };
	virtual void onPaint() { };
	virtual void onLButtonUp() { };
	virtual void onCommand(WPARAM wp, LPARAM lp) { };
	
	static LRESULT CALLBACK WindowProc(HWND h_wnd, UINT msg, WPARAM wp, LPARAM lp) {
		auto self = (DerivedWindow*)(GetWindowLongPtr(h_wnd, GWLP_USERDATA));

		if(self == NULL) {
			if(msg == WM_CREATE) {
				self = (DerivedWindow*)(((LPCREATESTRUCT)lp)->lpCreateParams);

				SetWindowLongPtr(h_wnd, GWLP_USERDATA, (LONG_PTR)self);
				self->h_wnd = h_wnd;

				self->onCreate();
			}

			return DefWindowProc(h_wnd, msg, wp, lp);
		}

		else {
			return self->mainProc(h_wnd, msg, wp, lp);
		}
	}

	LRESULT mainProc(HWND h_wnd, UINT msg, WPARAM wp, LPARAM lp) {
		switch(msg) {
		case WM_DESTROY:
			PostQuitMessage(0);
			return 0;
		case WM_LBUTTONUP:
			onLButtonUp();
			break;
		case WM_PAINT:
			onPaint();
			break;
		case WM_COMMAND:
			onCommand(wp, lp);
			break;
		}

		return DefWindowProc(h_wnd, msg, wp, lp);
	}

	HWND h_wnd;
	static const char* class_name;
};

class WindowException : std::exception {
public:
	class RegisterException;
};

class WindowException::RegisterException {

};

template <typename DerivedWindow>
const char* Window<DerivedWindow>::class_name;

S_JISの1文字ごと全文字読み取り

イテレータで全文字読み取ることを考えます。文字列返却のために、イテレータクラスのメモリを使っていますが、相当邪道だと思います。unique_ptrは使いたくないし、素直にポインタとサイズを返すのがいいのかもしれないが、簡単に使えないし困ったものです。

#include <stdio.h>
#include <memory>
#include <iterator>

class S_JIS_iterator;

class S_JIS {
	friend S_JIS_iterator;
public:
	S_JIS(const char* str) : str(str) {

	}
	S_JIS_iterator begin();
	S_JIS_iterator end();
private:
	// 2バイト・あるいは\0が必要
	static int getByteOfNextCharactor(const char* next) {
		const unsigned char next1 = next[0];
		
		if(next1 == '\0') {
			return 0;
		}
		
		const unsigned char next2 = next[1];

		if(next2 == '\0') {
			return 1;
		}
		else if(
			(
				(next1 >= 0x81 && next1 <= 0x9F) ||
				(next1 >= 0xE0 && next1 <= 0xEF)
			) && (
				(next2 >= 0x40 && next2 <= 0x7E) ||
				(next2 >= 0x80 && next2 <= 0xFC)
			)
			) {
			return 2;
		}
		else {
			return 1;
		}
	}

	std::string str;
};

class S_JIS_iterator : std::iterator<std::forward_iterator_tag, char*> {
	friend S_JIS;
public:
	S_JIS_iterator(S_JIS* self, int pos) : self(self), pos(pos) { }
	const char* operator*() const {
		const int n = self->getByteOfNextCharactor(&self->str[pos]);
		std::memcpy(moji, &self->str[pos], n);
		moji[n] = '\0';

		return moji;
	}
	bool operator==(const S_JIS_iterator& it) const {
		return pos == it.pos;
	}
	bool operator!=(const S_JIS_iterator& it) const {
		return pos != it.pos;
	}
	S_JIS_iterator& operator++() {
		pos += self->getByteOfNextCharactor(&self->str[pos]);
		return *this;
	}
	S_JIS_iterator operator++(int) {
		S_JIS_iterator ret = *this;
		operator++();
		return ret;
	}
private:
	S_JIS* self;
	mutable char moji[4]; // 返却用
	int pos;
};

S_JIS_iterator S_JIS::begin() {
	return S_JIS_iterator(this, 0);
}
S_JIS_iterator S_JIS::end() {
	return S_JIS_iterator(this, str.length());
}

int main() {
	S_JIS str("あAいBうCえDおE");

	for(const auto& it : str) {
		printf("%s\n", it);
	}

	getchar();
	return 0;
}