C/C++

確か古いバージョンのVisual Studioでは、exeのプロジェクトでプロジェクトの依存関係を設定するだけでよかったはずなんだけど、VS2017ではそれではダメみたいだ。
ということでVS2017での方法を書く。

ソリューションには、testlibというスタティックライブラリのプロジェクトと、testというexeのプロジェクトがあって、testプロジェクトのexeにtestlibプロジェクトで生成されるライブラリをリンクしたいものとします。

ソリューションエクスプローラーでtestプロジェクトの参照を右クリックしてポップアップメニューから参照の追加を選択する。

001

参照の追加ダイアログが表示されるのでtestlibにチェックを入れてokボタンを押す。

002

これでOKだ。



このエントリーをはてなブックマークに追加 Clip to Evernote
ブログランキング・にほんブログ村へ
にほんブログ村

C言語でやったUnicodeの正規化のC++版だ。C言語の時と同じようにICUを使う。ICUには、C++のAPIも用意されているのでそっちを使う。

こんな感じだ。

#include <stdio.h>
#include <stdlib.h>
#include "unicode/unistr.h"
#include "unicode/normalizer2.h"

void dump(char *mem, int size);

int main()
{
	icu::UnicodeString src("がぎぐげご", "utf-8");
	printf("src.length()=%d\n", src.length());
	dump((char*)src.getBuffer(), src.length() * sizeof(UChar));
	
	// (1) Unicodeの正規化(NFD)の準備
	UErrorCode err = U_ZERO_ERROR;
	const icu::Normalizer2 *normalizer = icu::Normalizer2::getNFDInstance(err);
	if(U_FAILURE(err)) {
		fprintf(stderr, "err=%d\n", err);
		exit(1);
	}

	// (2) 正規化する。
	err = U_ZERO_ERROR;
	icu::UnicodeString dest = normalizer->normalize(src, err);
	if(U_FAILURE(err)) {
		fprintf(stderr, "err=%d\n", err);
		exit(1);
	}
	printf("dest.length()=%d\n", dest.length());
	dump((char*)dest.getBuffer(), dest.length() * sizeof(UChar));
	

	return 0;
}


void dump(char *mem, int size)
{
	int addr, offset;
	
	for(addr = 0; addr < size; addr += 16) {
		printf("%04X ", addr & 0xffff);
		for(offset = 0; offset < 16; offset++) {
			if(addr + offset >= size) {
				break;
			}
			printf("%02X ", mem[addr + offset] & 0xff);
		}
		printf("\n");
	}	
}

(追記)
コンパイルは、こうだ。

$ g++ -Wall -o unicode2 `icu-config --cppflags --ldflags` unicode2.cpp

このエントリーをはてなブックマークに追加 Clip to Evernote
ブログランキング・にほんブログ村へ
にほんブログ村

UnicodeのライブラリICUを使ってUnicodeの正規化を行ってみる。
Unicodeの正規化とは、簡単に言うと見た目が「が」の文字を1つのコード「が」で表す場合と「か」+「゛」(濁点)の2つで表す場合がある。前者を合成文字で後者が結合文字って言う。これらを変換することを正規化っていう。(かなり適当な説明なんで詳しくはググってくれw)

とりあえず、ソースを示す。

#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include <string.h>
#include "unicode/ustring.h"
#include "unicode/unorm2.h"

void dump(char *mem, int size);

int main()
{
	char *src = "がぎぐげご";
	UErrorCode err;
	int32_t destLen;
	UChar *dest, *dest2;
	int32_t numSubstitutions;
	
	/* (1) UTF8からUnicodeへの変換後の文字数を取得する。 */
	err = U_ZERO_ERROR;
	u_strFromUTF8WithSub(NULL, 0, &destLen, src, -1, 0xfffd, &numSubstitutions, &err);
	printf("destLen=%d\n", destLen);
	
	/* (2) 変換後の文字列を入れるメモリを確保 */
	dest = (UChar*)calloc(destLen + 1, sizeof(UChar));
	if(!dest) {
		fprintf(stderr, "malloc error\n");
		exit(1);
	}
	
	/* (3) UTF8からUnicodeへ変換 */
	err = U_ZERO_ERROR;
	u_strFromUTF8WithSub(dest, destLen + 1, &destLen, src, -1, 0xfffd, &numSubstitutions, &err);
	if(U_FAILURE(err)) {
		fprintf(stderr, "err=%d\n", err);
		exit(1);
	}
	printf("destLen=%d\n", destLen);	
	dump((char*)dest, (u_strlen(dest) + 1) * sizeof(UChar));

	/* (4) Unicodeの正規化(NFD) */
	err = U_ZERO_ERROR;
	const UNormalizer2 *normalizer = unorm2_getNFDInstance(&err);
	if(U_FAILURE(err)) {
		fprintf(stderr, "err=%d\n", err);
		exit(1);
	}
	/* (5) 正規化後のサイズを取得 */
	err = U_ZERO_ERROR;
	int32_t destLen2 = unorm2_normalize(normalizer, dest, -1, NULL, 0, &err);
	printf("destLen2=%d\n", destLen2);
	
	/* (6) 正規化後の文字列を入れるメモリを確保 */		
	dest2 = (UChar*)calloc(destLen2 + 1, sizeof(UChar));
	if(!dest2) {
		fprintf(stderr, "malloc error\n");
		exit(1);
	}
	/* (7) 正規化する */
	err = U_ZERO_ERROR;
	destLen2 = unorm2_normalize(normalizer, dest, -1, dest2, destLen2 + 1, &err);
	if(U_FAILURE(err)) {
		fprintf(stderr, "err=%d\n", err);
		exit(1);
	}
	dump((char *)dest2, (u_strlen(dest2) + 1) * sizeof(UChar));
	
	free(dest2);
	free(dest);
	
	return 0;
}

void dump(char *mem, int size)
{
	int addr, offset;
	
	for(addr = 0; addr < size; addr += 16) {
		printf("%04X ", addr & 0xffff);
		for(offset = 0; offset < 16; offset++) {
			if(addr + offset >= size) {
				break;
			}
			printf("%02X ", mem[addr + offset] & 0xff);
		}
		printf("\n");
	}	
}

ソースはUTF-8で保存しておいて、コンパイルはこんな感じでする。

$ gcc -o unicode -I/opt/local/include -L/opt/local/lib -licuuc unicode.c

ICUは、MacPortsを使ってインストールしているものとする。

(1) UTF8からUnicodeへ変換した時の文字数を取得。変換できない文字があった場合は0xfffdにして変換するように指定している。また、ICUのAPIで渡すエラーコードが入る変数はU_ZERO_ERRORで初期化しておかないといけない。
(2) メモリ確保。
(3) UTF8からUnicodeへ変換
(4) 正規化の準備。ほんとは、合成文字列への変換を試したかったんだが結合文字列の「がぎぐげご」のUTF8を用意するのが面倒くさかったので、結合文字列の変換するとこにした。合成文字列への変換する場合はunorm2_getNFCInstance()を使う。
(5) 正規化後のサイズ取得。ここでやってる方法はドキュメントに書いてないので注意。
(6) メモリ確保。
(7) 正規化する。

(追記)
icu-configってのが用意されているようなので、コンパイルはこんな感じでする。

$ gcc -Wall -o unicode `icu-config --cflags --ldflags` unicode.c

このエントリーをはてなブックマークに追加 Clip to Evernote
ブログランキング・にほんブログ村へ
にほんブログ村


Addons Node.js v0.11.11 Manual & Documentation

こちらに書かれている。
けっこー、面倒くさそうだ(^^;) 


このエントリーをはてなブックマークに追加 Clip to Evernote
ブログランキング・にほんブログ村へ
にほんブログ村

他言語であるラムダ式だ。
書き方は、

[キャプチャ](引数リスト) ->戻り値型 { }

で、この関数の型は、

std::function<戻り値型 (引数型リスト)>

になる。あと、functionalヘッダをincludeする。

足し算するラムダを書いてみる。

std::function<int(int, int)> plus = [](int a, int b) -> int {
	return a + b;
};

std::cout << plus(1, 1) << std::endl; // "2"を表示

型推論使って、すっきりに。

auto plus = [](int a, int b) -> int {
	return a + b;
};

std::cout << plus(1, 1) << std::endl; // "2"を表示

キャプチャ
いわゆるクロージャを作るのに必要。ラムダ式を宣言したスコープ内のローカル変数をラムダ式内で使用することだ。
まー、書いてみればわかる。

#include "stdafx.h"
#include <iostream>
#include <functional>

std::function<int()> getTestFunc() {
	int hoge = 20;
	return[=]() { return hoge; };
}

int _tmain(int argc, _TCHAR* argv[])
{
	auto test = getTestFunc();

	std::cout << test() << std::endl; // "20"を表示

	return 0;
}

=と書くと、ラムダ式が宣言された段階での値がコピーされる。(コピーキャプチャ)
&と書くと、参照になる。(参照キャプチャ)

#include "stdafx.h"
#include <iostream>
#include <functional>

std::function<int()> getTestFunc() {
	int hoge = 20;
	return[&]() { return hoge; };
}

int _tmain(int argc, _TCHAR* argv[])
{
	auto test = getTestFunc();
	
	std::cout << test() << std::endl; // 変な値が表示される。
	
	return 0;
}

参照だと、getTestFuncからリターンした段階でローカル変数のhogeは無くなっているので変なところを参照していることになり、変な値が表示される。やっちゃ、いけない。考えて使おー。

変数を個別にコピーキャプチャする場合は、[]内に[hoge1, hoge2]のように変数名をカンマ区切りで書く。参照キャプチャする場合は、[&hoge1, &hoge2]のように書く。[hoge1, &hoge2]のようにコピーキャプチャの変数と参照キャプチャの変数も混ぜれる。hoge1は参照キャプチャでそれ以外はコピーキャプチャの場合は[=,&hoge1]と書く。

JavaScriptのクロージャの説明でよくあるカウントアップする関数を返すのを書いてみる。

std::function<int()> getCounter()  {
	int counter = 0;
	return[counter]() { return ++counter; };
}

けど、エラーになる。キャプチャした変数が変更できないみたいだ。
変更するには、ラムダ式mutableをつける。

#include "stdafx.h"
#include <iostream>
#include <functional>

std::function<int()> getCounter()  {
	int counter = 0;
	return[counter]() mutable { return ++counter; };
}

int _tmain(int argc, _TCHAR* argv[])
{
	auto countUp = getCounter();
	
	std::cout << countUp() << std::endl; // "1"を表示
	std::cout << countUp() << std::endl; // "2"を表示
	std::cout << countUp() << std::endl; // "3"を表示
	std::cout << countUp() << std::endl; // "4"を表示
	
	return 0;
}

このエントリーをはてなブックマークに追加 Clip to Evernote
ブログランキング・にほんブログ村へ
にほんブログ村

↑このページのトップヘ