Mac OS X で boost::test するときは <code>#define BOOST_TEST_DYN_LINK</code> が必須

Mac OS X はスタティック リンクをサポートしていないため,boost::test するときは #define BOOST_TEST_DYN_LINK が必須のようです.

経緯

boost.test をちょっと試そうと思っていくつかちょっとだけ実験してた.

参考にしたのはとりあえずここらへん.

で,スタティック リンクでも試そうと

#define BOOST_TEST_DYN_LINK

を消して,

% g++ test.cpp -lboost_unit_test_framework test.cpp -static

としたのだけど,

ld: library not found for -lcrt0.o
collect2: ld returned 1 exit status

というエラーがでてコンパルができなかった.

ちょっと調べてみると,以下のようなことが公式に宣言されてた.

Mac OS X では、ユーザバイナリの静的なリンクはサポートされていません。

Mac OS X-lcrt0.o でエラーがでたら,素直にダイナミック リンクにしてあげましょう.

@property を使ってみる

はじめの一歩

まずは一番簡単な使い方から.

#import <Foundation/NSObjCRuntime.h>
#import <Foundation/NSString.h>

@interface MyObject : NSObject
{
  NSString* value;
}
@property(copy, readwrite) NSString* value;
@end

@implementation MyObject
@synthesize value;
@end

int main() {
  MyObject* obj = [MyObject alloc];

  [obj setValue:@"Hello, World!"];

  NSLog(@"%@", [obj value]);

  return 0;
}

@interface@property を宣言しておいて,@implementation のところの @synthesizesetValuevalue という 2 つのメソッドを作ってくれる.

あと,obj.value (ドット構文) でもアクセスできちゃう.

インスタンス変数とプロパティの名前を違うものにする

インスタンス変数名とプロパティ名を変えたい場合,@synthesize {プロパティ名} = {メンバ変数} ってやれば OK

#import <Foundation/Foundation.h>

@interface MyObject : NSObject
{
 @public
  NSString* memberValue;
}
@property(copy, readwrite) NSString* value;
@end

@implementation MyObject
@synthesize value = memberValue;
@end

int main() {
  MyObject* obj = [MyObject alloc];
  [obj setValue:@"Hello, World!"];

  NSLog(@"access use property: %@", [obj value]);
  NSLog(@"access directory: %@", obj->memberValue);

  return 0;
}

setter と getter を自分で定義する

#import <Foundation/Foundation.h>

@interface MyObject : NSObject
{
 @public
  NSString* value_;
}
@property(copy, readwrite) NSString* value;
@end

@implementation MyObject
@dynamic value;

- (NSString *)value {
  NSLog(@"in value getter");
  return value_;
}

- (void)setValue:(NSString*) newValue {
  NSLog(@"in value setter");
  value_ = [newValue copy];
}
@end

int main() {
  MyObject* obj = [MyObject alloc];
  [obj setValue:@"Hello, World!"];

  NSLog(@"access use property: %@", obj.value);

  return 0;
}

@implementation の中で @dynamic value ってやっておけば OK

プロパティの動作とかの指定

プロパティの宣言属性っていうのでプロパティの細かい動作を指定できる.

@prpoerty(attributes)attributes のところをいろいろ指定できる.

ざっとまとめる.

getter={getterName}
getter の名前を {getterName} に指定.デフォルトはプロパティ名と同じ.これは,Key Value Coding の規約に従っている.引数なしでプロパティと同じ型を返す.
setter={setterName}
setter の名前を {setterName} に指定.デフォルトは setプロパティ名.同じく,Key Value Coding の規約に従っている.プロパティと同じ型の引数で何も返さない (void を返す)
readonly
読み取り専用.setter は定義されない.ドット記法で値を代入しようとすると怒られる.
readwrite
読み書き可能.デフォルトはこれ.getter と setter の両方定義しないとダメ.
assign
setter で単純代入を使用するようになる.デフォルトはこれ.
retain
setter で retain される.
copy
setter で copy がセットされるようになる.
noatomic
アクセッサが非アトミックになる.マルチスレッド関連.

おわり

だいたいこれくらいでプロパティ使えるようになった!

他の機能とか仕様は必要になったときにその都度みれば良さそうだ

Hello, World! by Objective-C + Foundation Framework

Objective-C + Foundation Framework な Hello, World! を作る.

GC 使わない場合

#import <Foundation/NSString.h>
#import <Foundation/NSAutoreleasePool.h>
#import <stdio.h>

int main(int argc, char **argv) {
    NSAutorelesePool* pool = [[NSAutoreleasePool alloc] init];

    NSString* str = [[NSString alloc] initWithString:@"こんにちは,世界!\n"];
    const char* cstr = [str UTF8String];

    printf("%s", cstr);

    [pool release];

    return 0;
}


NSAutoreleasePool は Foundation を使う上でメモリ管理用に必要なもの.

これがないと,NSString の中で autorelease か release か何かが呼ばれるらしいので実行時におこられちゃう.

    NSAutorelesePool* pool = [[NSAutoreleasePool alloc] init];

NSString を直接標準出力するものはないみたいなので,いったん C 言語の文字列に変換する必要がある.
ここでは,UTF-8 なマルチ バイト文字列に変換してる.

    const char* cstr = [str UTF8String];

NSString は wchar_t みたいなワイド文字列に変換する方法はないみたい.

コンパイルと実行はこんな感じ.

% gcc -framework Foundation test.m 
% a.out    
こんにちは,世界!

GC 使う場合

GC を使う場合は NSAutorelesePool はいらないみたい.

#import <Foundation/NSString.h>
#import <stdio.h>

int main() {
    NSString* str = [[NSString alloc] initWithString:@"こんにちは,世界!\n"];
    const char* cstr;
    cstr = [str UTF8String];

    printf("%s", cstr);

    return 0;
}

コンパイルに -fobjc-gc-fobjc-gc-only が必要になる.

% gcc -fobjc-gc -framework Foundation test.m 
% a.out    
こんにちは,世界!

とりあえずこれで Objective-C で Hello, World! をかけるようになったぞ!

Objective-C の文法を覚える!

JavaScript の文法はだいたいわかったので,Objective-C を覚える!

コマンドラインでコンパイルできるようにしてから,
Page Not Found - Apple Developer
ここで覚えてくのが今の予定.

ターミナルでコンパイル

まずは,UbuntuでObjective-Cをコンパイルする - 0xDB を参考に素の Objective-C をコンパイルしてみる.

何をインクルードする必要があったのか忘れたので Objective-Cでダックタイピング - @kei10in の日記 を参考にコード書いて実行してみたけど,illegal hardware instruction っていうのが出てだめだ.

とりあえず,これは後で調べる.

#import <stdio.h>
#import <objc/Object.h>

int main() {
    printf("Hello, World!\n");
    return 0;
}

をコンパイルして実行した.

% gcc -lobjc test.m
% a.out
Hello, World!

前途多難そうだけど,とりあえず進む.


とは言ったもののちょっとしらべる.

#import <Foundation/NSObject.h>
#import <stdio.h>

@interface HelloWorld : NSObject
- (void) hello;
@end

@implementation HelloWorld
- (void) hello { printf("Hello, World!\n"); }
@end


int main(int argc, char **argv){
    id obj = [HelloWorld alloc];;

    [obj hello];

    return 0;
}

を↓みたいにコンパイル,実行したら問題なかった.

% gcc -framework Foundation test.m
% a.out
Hello, World!

しっかり調べたわけじゃないので微妙だけど,Mac だと objc/Object.h の Object を使うと簡単には実行できなそう.

素直に Foundation/NSObject.h を使うことにする.

Apple の Objective-C 2.0 プログラミング言語

Apple の Objective-C の解説を読み進める.

最初はあんまり面白くないお話.だらだら読み進める.

型のイントロスペクション

やっとコードがかける!!

NSObject では isMemberOfClass,isKindOfClass というのでクラスの型情報とかの検証ができるみたい.

Python で言うところの isinstance とかかな?

とりあえず使ってみる.

#import <Foundation/NSObject.h>
#import <stdio.h>

@interface Speaker : NSObject
- (void)speak;
@end

@implementation Speaker
- (void)speak { printf("I'm speaker.\n"); }
@end

@interface RoudSpeaker : Speaker
@end

@implementation RoudSpeaker
- (void)speak { printf("I'M SPEAKER!!!\n"); }
@end


void say(Speaker* speaker) {
    if ( [speaker isKindOfClass:Speaker] ) {
        [speaker speak];
    }
    if ( [speaker isMemberOfClass:RoudSpeaker] ) {
        [speaker speak];
    }
}

int main(int argc, char **argv){
    Speaker* speaker1 = [Speaker alloc];
    Speaker* speaker2 = [RoudSpeaker alloc];

    say(speaker1);
    
    printf("\n");

    say(speaker2);
    
	return 0;
}

そしてコンパイル.

% gcc -framework Foundation test.m                      
test.m: In function ‘say’:
test.m:22: error: expected expression before ‘Speaker’
test.m:25: error: expected expression before ‘RoudSpeaker’

ありゃ! エラーだ.

ちょっと調べる.

にサンプルがあった.

if ([obj1 isMemberOfClass:[NSObject class]]){
NSLog(@"YES");
}else{
NSLog(@"NO");
}

サンプルによると引数として [NSObject class] みたいな感じにしないといけないらしい.

say 関数をちょっと書き換える.

void say(Speaker* speaker) {
    if ( [speaker isKindOfClass:[Speaker class]] ) {
        [speaker speak];
    }
    if ( [speaker isMemberOfClass:[RoudSpeaker class]] ) {
        [speaker speak];
    }
}

やたー,出来た!

% gcc -framework Foundation test.m
% a.out
I'm speaker.

I'M SPEAKER!!!
I'M SPEAKER!!!
いったんここまで.

読んでてもコード書いて試すみたいなことがやりにくいわ…

先に NSString を覚えよう…

セクション番号を自動で振ってくれる JavaScript

JavaScript の勉強のつもりで作ってみました.

表示例

     <h1>ABC</h1>
     <h2>foo</h2>
     <h2>bar</h2>
     <h3>hoge</h3>
     <h3>fuga</h3>
     <h1>XYZ</h1>
     <h3>piyo</h3>

f:id:kei10in:20100519223036p:image

こんな感じのものが,

f:id:kei10in:20100519224733p:image

こんな風になります.

コード

var HeaderEnumerator = function() {
    this.enumeratingTags = ['H1', 'H2', 'H3', 'H4', 'H5', 'H6'];
    this.sectionLevel = new SectionLevel();
};

HeaderEnumerator.prototype = {
    startElement: function(element) {
        var newSectionLevel = this.calcSectionLevel(element);
        if (newSectionLevel < 0) {
            return;
        }
        this.setSectionLevel(newSectionLevel);
        this.preppendSectionNumber(element);
    },
    endElement: function(element) {
    },
    calcSectionLevel: function(element) {
        return this.enumeratingTags.indexOf(element.nodeName);
    },
    preppendSectionNumber: function(element) {
        var numElem = document.createElement('span');
        numElem.className = 'section-number';
        var numText = document.createTextNode(this.formatSectionNumber());
        numElem.appendChild(numText);
        element.insertBefore(numElem, element.firstChild);
    },
    setSectionLevel: function(newSectionLevel) {
        this.sectionLevel.setLevel(newSectionLevel);
    },
    formatSectionNumber: function() {
        return this.sectionLevel.format();
    }
};


var SectionLevel = function() {
    this.numbers = [new SectionNumber()];
};

SectionLevel.prototype = {
    indent: function() {
        this.numbers.push(new SectionNumber());
    },
    unindent: function() {
        this.numbers.pop();
    },
    increment: function() {
        this.getCurrentNumber().increment();
    },
    setLevel: function(level) {
        if (level < 0) {
            return;
        }
        if (level < this.getLevel()) {
            var n = this.getLevel() - level;
            for (var i = 0; i < n; i++) {
                this.unindent();
            }
        } else if (this.getLevel() < level) {
            var n = level - this.getLevel();
            for (var i = 0; i < n; i++) {
                this.indent();
            }
        }
        this.increment();
    },
    getLevel: function() {
        return this.numbers.length - 1;
    },
    getCurrentNumber: function() {
        return this.numbers[this.getLevel()];
    },
    getNumber: function(i) {
        return this.numbers[i];
    },
    format: function() {
        var numstr = '';
        for (var i = 0; i < this.numbers.length; i++) {
            numstr += this.getNumber(i).get() + '.';
        }
        return numstr + ' ';
    }
};


var SectionNumber = function() {
    this.sectionNumber = 0;
};

SectionNumber.prototype = {
    increment: function() {
        this.sectionNumber++;
    },
    get: function() {
        return this.sectionNumber;
    }
};



function walk(element, obj) {
    obj.startElement(element);
    var len = element.childNodes.length;
    for (var i = 0; i < len; i++) {
        walk(element.childNodes[i], obj);
    }
    obj.endElement(element);
}

window.addEventListener("load",
                        function () {
                            walk(document.body, new HeaderEnumerator());
                        },
                        false);

全然 JavaScript っぽくないような気がしますが…

感想

基本的な文法はだいたいわかった気がします.

あと,見やすいライブラリ リファレンスも手に入れました.

Core JavaScript 1.5 ガイド - MDN


JavaScript を面白いと感じるには,JavaScript っぽい書き方をしてこそだと思う (Python は内包表現が面白いよね) のだけど,JavaScript っぽく書けた気がしません...

今更だけど JavaScript 勉強を始めるよ!

ベースになる HTML ファイルの準備

まずはベースにする HTML ファイルを作る.
とりあえず内容は↓

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html lang="ja">
  <head>
    <meta http-equiv="content-type" content="text/html; charset=utf-8">
    <meta http-equiv="content-style-type" content="text/css">
    <meta http-equiv="content-script-type" content="javascript">
    <title>タイトル</title>
    <link rel="stylesheet" type="text/css" href="style.css">
    <script type="text/javascript" src="./main.js"></script>
  </head>
  <body>
  </body>
</html>


この 1 行を追加しておいて外部ファイルを使えるようにしておく.

    <script type="text/javascript" src="./main.js"></script>

Hello, World!

main.js を編集して Hello, World! を作ろー

alert で Hello, World!

alert 関数をつかってみる.

alert("Hello, World!");

こんなダイアログが表示された!!

f:id:kei10in:20100509224312p:image

document.write を使ってみる

document.write っていうのを使ってみる.

document.write("Hello, World!");

こんな風に表示された.

f:id:kei10in:20100509225025p:image

どこに write されるのか試す
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html lang="ja">
  <head>
    <meta http-equiv="content-type" content="text/html; charset=utf-8">
    <meta http-equiv="content-style-type" content="text/css">
    <meta http-equiv="content-script-type" content="javascript">
    <title>Test JavaScript</title>
    <link rel="stylesheet" type="text/css" href="style.css">
    <script type="text/javascript">
      <!--
         document.write("Hello, World!");
         // -->
    </script>
  </head>
  <body>
    <h1>foobar</h1>
  </body>
</html>

f:id:kei10in:20100509225026p:image

タグを追加してみる.
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html lang="ja">
  <head>
    <meta http-equiv="content-type" content="text/html; charset=utf-8">
    <meta http-equiv="content-style-type" content="text/css">
    <meta http-equiv="content-script-type" content="javascript">
    <title>Test JavaScript</title>
    <link rel="stylesheet" type="text/css" href="style.css">
    <script type="text/javascript">
      <!--
         document.write("<h1>Hello, World!</h1>");
         // -->
    </script>
  </head>
  <body>
    <h1>foobar</h1>
  </body>
</html>

f:id:kei10in:20100509230156p:image

Event Listener を使って Hello, World!
window.addEventListener("load", function () {
                              elem = document.createElement("p");
                              elem.textContent = "Hello, World!";
                              document.body.appendChild(elem);
                          }, false);

f:id:kei10in:20100510005314p:image

まとめ

今日は 3 種類の Hello, World! を作ってみました.

おなかが空いた & 眠いので今日は寝ます.

[Windows] 7/Vista と XP のデュアル ブート環境構築

最近 Win7 と HDD を買ったので XP とのデュアル ブート環境構築をしたので,
設定とかをまとめておきます.

特に Web で調べてみた方法だけでは,Widows XP を起動しようとしたときに強制再起動されてしまったので,その問題解決についても述べます.

構成

  • HDD1: パーティション 1: Windows 7 のインストーラが勝手に作る 100 MB の領域
  • HDD1: パーティション 2: Windows 7
  • HDD1: パーティション 3: データ
  • HDD2: パーティション 1: Widnows XP
  • HDD2: パーティション 2: データ

です.

Windows XPWindows 7 を異なる HDD に単独でインストールしています.

Windos 7 用ブート マネージャの設定

まず Widows 7 用のブート マネージャの設定を行います.

いろんなところにも書かれているように以下の用にコマンドを実行します.

コマンドプロンプトは管理者として実行する必要があります.

> bcdedit /create {ntldr} /d "Windows XP"
> bcdedit /set {ntldr} device partition=C:
> bcdedit /set {ntldr} path \ntldr
> bcdedit /displayorder {ntldr} -addlast

"partition=C:" の "C:" は必ず "C:" である必要があるみたいです.

Windows 7 を D ドライブとかにインストールしていても,たぶん "C:" じゃないとダメです.

Windows XP 用ブート ローダの設定

まず,Windows 7 のインストーラが勝手に作る 100 MB のパーティション と Windows XP がインストールされているパーティションを操作できるようにします.
(BIOS の設定を変えたり,HDD の接続順を変えて Windows XP を起動すればいいと思います.)

100 MB のパーティション (bootmgr のある場所) に XP の C ドライブ直下にある,ntldr, NTDETECT.COM, bootfont.bin, boot.ini をコピーします.

bootmgr のある場所以外の場所に ntldr などのファイルを置いて,ブート マネージャの設定をそれに合わせて変更しても,Windows XP は起動できないので注意してください.


boot.ini を以下の用に変更します.

[boot loader]
timeout=30
default=multi(0)disk(0)rdisk(1)partition(1)\WINDOWS
[operating systems]
multi(0)disk(0)rdisk(1)partition(1)\WINDOWS="Microsoft Windows XP Professional" /noexecute=optin /fastdetect

rdisk の () の中の値を変更しています.

起動

以上で設定は完了です.

あとは,先頭の HDD を Windows 7 のものにして起動すれば Windows 7 と Windows XP とのデュアル ブートができると思います.

まとめ

自分の場合,今 Web で簡単に調べることができる情報だけでは Windows 7 と Windows XP とのデュアル ブートはできませんでした.

その対策として,bootmgr のある場所に ntldr をおくことで問題は解決されました.

人によって Windows 7 や Widows XP のインストールされている状況は異なると思いますが,ntldr を bootmgr と同じ場所におくことでデュアル ブート可能になると思います.