読者です 読者をやめる 読者になる 読者になる

const int よりも int const と書こう、という提案

c++

……という提案を、試しにちょっとしてみます。

ポインタ型とconst

C++er の皆さん、次のコードを正しく説明できるでしょうか?

int hoge = 42;
const int * p1 = &hoge;
int const * p2 = &hoge;
int * const p3 = &hoge;

もちろん答えはこうなります:

int hoge = 42;
const int * p1 = &hoge; // p1の指示先を書き換える事はできないが、p1の指示先自体は変更可能
int const * p2 = &hoge; // p1と同じ
int * const p3 = &hoge; // p3の指示先を書き換える事はできるが、p3の指示先自体は変更不能

つまり、const*の前後どちらにあるかで意味が変わってくるわけですね。
この仕様は、知っている人にとっては不思議でも何でもないのですが、知らないと極めて不可解に見えるどころかソースコードをちゃんと日本語に直せません。困った。

と、そんなわけで今回の提案ですよ!

Oh! Japanese!!

試しに、こんなマクロを定義してみましょう。

#define の不変型 const
#define へのポインタ型 *

これを使うと先程のコードは、

const int へのポインタ型 p1 = &hoge; // p1の指示先を書き換える事はできないが、p1の指示先自体は変更可能
int の不変型 へのポインタ型 p2 = &hoge; // p2の指示先を書き換える事はできないが、p2の指示先自体は変更可能
int へのポインタ型 の不変型 p3 = &hoge; // p3の指示先を書き換える事はできるが、p3の指示先自体は変更不能

となり、いずれの場合も*pが書き換え可能かどうか、あるいはpが変更可能かどうかが一目瞭然になりました。
同様に、

#define の最適化抑止型 volatile
#define への参照型 &
#define の一時オブジェクトへの参照型 &&

のようにもできます。これで「ポインタへの参照って*&だっけ? それとも&*だったっけ?」みたいな混乱からもオサラバ! 「ポインタ型への参照型」なんだから* &に決まってます!

なお、mutableconstvolatileとは性質の異なるキーワードなので、必ず先頭に来なくてはなりません。

で、なんでint constがいいの?

そんなわけで最初の提案に戻ります。

既に答えてしまったようなものですが、constvolatile*&&&も、全て「既存の型に後ろから修飾する形で新しい型を作る」事ができるキーワードです。
が、constvolatileは既存の型の直後に来る場合に限り、「既存の型の前から修飾する形で新しい型を作れ」ます。

もちろん、せいぜい「 const 整数へのポインタ」とか「整数への const ポインタ」とか言っていれらるうちはいいのですが、「 const 整数型への const ポインタのポインタの参照」と言われてconst int * const * &とかさらっと出てくるのは相当の手馴れでしょう。
なのでここは、「整数型の不変型へのポインタ型の不変型へのポインタ型への参照型」と呼ぶ事にしましょう。それで、int const * const * &と書きましょう……ほうら誰でも一発でわかる。

でも、本当は

正直、C++らしく、と思うとこんな風に書きたい:

pointer<add_const<int>::type>::type p1 = &hoge; // p1の指示先を書き換える事はできないが、p1の指示先自体は変更可能
add_const<pointer<int>::type>::type p3 = &hoge; // p3の指示先を書き換える事はできるが、p3の指示先自体は変更不能

でも面倒だから普通にconst*使えばいいや。

問.Cでオブジェクト指向プログラミングを行なえ

C

問.Cでオブジェクト指向プログラミングを行なえ。ただし「オブジェクト指向プログラミング」とは、次のような特徴を持つプログラミング技法であるものとする:

  1. オブジェクトの実装はオブジェクトのユーザーからは隠蔽される(カプセル化/隠蔽)
  2. 同一型のオブジェクトと同一メソッドを与えた時、実際のメソッドの動作はオブジェクトの内容により変化する(ポリモーフィズム多態性

なお、ユーザーが既存のオブジェクトをカスタマイズして新たなオブジェクトを作成する機能は、必要ないものとする。

この問いの狙い

よく、「オブジェクト指向プログラミング」と「オブジェクト指向言語」は混同されます。が、前者はプログラムを設計する上での考え方で、後者はその考え方を容易にソースコードに書けるような仕様になっている言語の事で、全く違うものを指しています。
その証拠を示すため、「非オブジェクト指向言語」たるC言語で「オブジェクト指向プログラミング」をしてみよう、というのが今回の狙いですね。よくあるネタですのでわざわざ当ブログで取り上げるまでもないかもしれませんが、弊社内にもわかっていない人がいたのでメモ代わりに。

先に述べておきますと、Windows API が実は、実際にC言語オブジェクト指向プログラミングを行なった実例の一つになっています。試しに下記のソースコードHWNDを使ってウィンドウを操作するプログラムを比較してみると、よくおわかりになるでしょう(実際のHWNDはセキュリティ的な問題((かつて、上手くデータを作ってそのアドレスをHWNDとして渡すと、Windows カーネルの権限で任意のコードが実行できるというセキュリティホールがあったようです))を回避するため、単純なポインタではありませんが)。

Cでカプセル化

……などと大仰な章題をつけましたが、C言語が使えると自称するのであれば当然使えて欲しいテクニック。
ヘッダーファイルでは構造体をポインタとしてのみ宣言し、ソースファイルで具体的な構造を定義、全ての構造体操作を専用の関数で行なう手法です。

/* oopforc.h */
#ifndef OOP_FOR_C_H__INCLUDED
#define OOP_FOR_C_H__INCLUDED

typedef struct tagObject *Object;

Object CreateObject(int hoge);
void DeleteObject(Object obj);
int GetHoge(Object obj);
void SetHoge(Object obj, int newhoge);
void PrintObject(Object obj);

#endif /* OOP_FOR_C_H__INCLUDED */
/* oopforc.c */
#include "oopforc.h"
#include <stddef.h>
#include <stdio.h>

struct tagObject
{
    int m_hoge;
};

Object CreateObject(int hoge)
{
    Object obj = malloc(sizeof(struct tagObject));
    if (!obj) return NULL;
    obj->m_hoge = hoge;
    return obj;
}
void DeleteObject(Object obj)
{
    free(obj);
}
int GetHoge(Object obj)
{
    return obj->m_hoge;
}
void SetHoge(Object obj, int newhoge)
{
    obj->m_hoge = newhoge;
}
void PrintObject(Object obj)
{
    printf("PrintObject: %d\n", obj->m_hoge);
}

使用法としてはこうなります:

/* main.c */
#include "oopforc.h"
#include <stdio.h>

int main(void)
{
    Object obj = NULL;

    obj = CreateObject(5);
    if (!obj) return 1;

    PrintObject(obj);   // PrintObject: 5
    printf("printf: %d\n", GetHoge(obj));   // printf: 5

    SetHoge(obj, 10);

    PrintObject(obj);   // PrintObject: 10
    printf("printf: %d\n", GetHoge(obj));   // printf: 10

    DeleteObject(obj);

    return 0;
}

実際は、各種の関数内で「与えられたobjが有効かどうか」をチェックする方が安全ですが、その辺りは今回は割愛します。

Cでポリモーフィズム

今回の課題は「既存クラスを継承したクラスをユーザー定義できる必要はない」という事にしましたので、仮想関数を使ったC++風のポリモーフィズムまでは不要でしょう。大抵はコールバック関数で事足りるはずです。
ですので、struct tagObjectに型タイプを示す列挙体を作り、実行時型を共用体に入れておけば十分にポリモーフィズムが実現できるでしょう。

……と言うと「あれっ?」と思う方もいらっしゃると思うのでちょっと補足しておきます。
ふだん「ポリモーフィズム」と言うと継承を思い浮かべがちですが、同じ書き方でオブジェクトに応じて動作さえ変わればとりあえずはポリモーフィズム。具体的な実装が継承なのかダックタイピングなのかテンプレートなのか、あるいは原始的なswitchによる分岐なのかは、オブジェクトのユーザーにとってはさっぱり関係のない話です。

/* oopforc.h */
#ifndef OBJECT_H__INCLUDED
#define OBJECT_H__INCLUDED

typedef struct tagObject *Object;

Object CreateIntObject(int value);
Object CreateStrObject(const char *value);
void DeleteObject(Object obj);
void PrintObject(Object obj);

#endif /* OBJECT_H__INCLUDED */
/* oopforc.c */
#include "oopforc.h"
#include "private/IntObject.h"
#include "private/StrObject.h"
#include <stddef.h>
#include <stdlib.h>

static enum EObjectType
{
    OBJ_INT,
    OBJ_STR,
};

struct tagObject
{
    enum EObjectType m_type;
    union
    {
        struct tagIntObject m_intobj;
        struct tagStrObject m_strobj;
    };
};

Object CreateIntObject(int value)
{
    Object obj = malloc(sizeof(struct tagObject));
    if (!obj) return NULL;

    obj->m_type = OBJ_INT;
    if (!InitializeIntObject(&obj->m_intobj, value))
    {
        free(obj);
        return NULL;
    }
    return obj;
}
Object CreateStrObject(const char *value)
{
    Object obj = malloc(sizeof(struct tagObject));
    if (!obj) return NULL;

    obj->m_type = OBJ_STR;
    if (!InitializeStrObject(&obj->m_strobj, value))
    {
        free(obj);
        return NULL;
    }
    return obj;
}

void DeleteObject(Object obj)
{
    if (!obj) return;

    switch (obj->m_type)
    {
    case OBJ_INT:
        TerminateIntObject(&obj->m_intobj);
        break;
    case OBJ_STR:
        TerminateStrObject(&obj->m_strobj);
        break;
    default:
        return;
    }

    free(obj);
}

void PrintObject(Object obj)
{
    switch (obj->m_type)
    {
    case OBJ_INT:
        PrintIntObject(&obj->m_intobj);
        break;
    case OBJ_STR:
        PrintStrObject(&obj->m_strobj);
        break;
    default:
        return;
    }
}
/* main.c */
#include "OOPforC.h"

int main(void)
{
    Object intobj = NULL, strobj = NULL;

    intobj = CreateIntObject(5);
    if (!intobj) return 1;
    
    strobj = CreateStrObject("hoge");
    if (!strobj) {
        DeleteObject(intobj);
        return 2;
    }

    PrintObject(intobj);
    PrintObject(strobj);

    DeleteObject(intobj);
    DeleteObject(strobj);

    return 0;
}

後は private/XXXObject.h 、private/XXXObject.c をご自由にご実装下さい。お好みで、CreateXXXObject()で確保するメモリサイズを引数に応じて変更できるように(struct tagStrObject{ char m_str[0]; };みたいな形式になってる場合)。

余談

こいつを真面目に「Cでもユーザー定義の継承とか動的型情報の取得とかできるようにしたいんだー!」とか言い出しちゃった人達が某 Micr○soft とかいう会社にいて、結果として生まれたのが COM なる技術でした。
もっとも、GUID をレジストリに登録しておけば自動的に DLL を探し出して関数定義を動的リンクできるよねー、的な発想まで加わっているせいで、初見では何やってるのかさっぱりわからないものに見えますが。

C実践プログラミング 第3版

C実践プログラミング 第3版

Javascriptを使ってiOSのWebViewからネイティブにデータを返そう

Objective-c Javascript WebView

Swift使っていますか?弊社ではまだほとんど使っていません。今日もObjective-Cの話題です。

iOSアプリを作るとき、すでにWeb版で実装されている機能をiOSでもそのまま使いたいがためにUIWebViewを使うということがあると思います(あると言ったらある)。そしてアプリケーションが複雑になってくるとそのUIWebViewとデータのやりとりをしたくなってくることがあるわけです。iOSからWebViewにデータを渡すのは比較的簡単で、シンプルな場合はURLにパラメータをつけてGETリクエストを投げてあげれば目的は達成できます。

逆にWebViewからデータをiOS側に戻すときが少々ややこしくなります。WebViewからiOSにPUSH的にデータを渡すことはほぼできないと思われるので、このケースではiOS側からデータを取りにいかなければいけません。

幸い、WebViewには読み込みが始まったときや終わったときなどを検知するためのコールバックが用意されています。例えば読み込み完了時にWebViewのデータを読み取ってあげればよいわけです。

- (void)webViewDidFinishLoad:(UIWebView *)webView
{
    NSString *body = [webView stringByEvaluatingJavaScriptFromString:@"document.body.innerHTML"];
    NSLog(@"html body: %@", body);
}

上のようにWebViewのJavascriptを呼べるので、document経由でHTMLのドキュメントを取り出していくことが可能です。というか、Javascriptを呼べるということはそもそも自由度が高いわけで結構なんでも出来てしまいますね。

JSON文字列を返す

WebViewで何か処理した結果をJSONとしてiOS側で使いたいというケースがあるとしましょう。そういうときはHTMLに<script>タグでJavascriptの関数を定義してあげます。

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
        <title>Untitled Document</title>
    </head>
    
    <body>
        <script type="text/javascript">
            function getJson() {
                var json = {
                    "name": "CFlat",
                    "address": {
                        "prefecture":"Shizuoka",
                        "city":"Hamamatsu"
                    }
                };
                return JSON.stringify(json);
            }
        </script>
    </body>
</html>

あとは先ほど使ったものと同じようにstringByEvaluatingJavaScriptFromStringを使ってこの関数を呼び出せばOK。

- (void)webViewDidFinishLoad:(UIWebView *)webView
{
    NSString *jsonString = [webView stringByEvaluatingJavaScriptFromString:@"getJson()"];
    NSData *jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
    NSDictionary *json = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingAllowFragments error:nil];
    NSLog(@"json: %@", json);

}

出力されるログはこうなりました。

2014-10-07 13:13:52.674 MyApp[20993:1596809] json: {
    address =     {
        city = Hamamatsu;
        prefecture = Shizuoka;
    };
    name = CFlat;
}

WebViewで表示する側も自分たちで担当できるのであればこの方法は自由度が高くて便利ではないでしょうか。既存のWeb資産の活用するのにも役立ちそうですね。

openFrameworksを使ったフレーム編集

C++ OpenCV openFrameworks

概要

動画からフレームを抜き出してOpenCVで加工する環境をopenFrameworksで準備してみました。
日本語パス周りで少しハマったのでメモ代わりにブログに残しておきます。

動作環境

完成図

  • 日本語の動画ファイルを読み込み、各フレームを反転して表示する
  • キーフレームの戻る、進むをキーボードから操作する

f:id:cflat-inc:20141003145832j:plain

作成手順

1. openframeworksをダウンロード

windows download openFrameworks for visual studioからダウンロード。
まずはopenFrameworksLibのコンパイルが必要です。 詳細はこちらのリンク先 を参照して下さい。
libs\openFrameworksCompiled\project\vs\openframeworksLib.vcxprojでプロジェクトを起動し、メニューからビルド>バッチビルドを選択しDebugとReleaseのビルドにチェックをいれてビルドを実行します。

2. プロジェクトの作成

今回作成するプロジェクト名をmyVideoとします。
projectGenerator\projectGenerator.exeをダブルクリックすると簡単にプロジェクトが作成出来ます。

f:id:cflat-inc:20141003145844j:plain

3. 動画再生

動画再生するにはofVideoPlayerを使います。
examples\video\videoPlayerExampleにサンプルがありますが、最小限のコードで動かすためには以下の設定で十分です。

ofApp.hに以下のように追記します。

class ofApp : public ofBaseApp{
public:
//...
private:
  ofVideoPlayer _player;
}

ofApp.cppに読み込む動画ファイルの指定、キーボードの矢印でフレームの移動が出来るように設定します。
動画はexamples\video\videoPlayerExample\bin\data\movies\fingers.movを使っています。

void ofApp::setup(){
  ofBackground(255,255,255);
  ofSetVerticalSync(true);
  _player.loadMovie("movies/fingers.mov");
  _player.play();
}

void ofApp::update(){
  _player.update();
}

void ofApp::draw(){
  _player.draw(0,0);
}

void ofApp::keyPressed(int key){
  switch(key){
  case 'f':
    // 連続再生 or フレームごとの再生
    _player.setPaused(!_player.isPaused());
    break;
  case OF_KEY_LEFT:
    // 前フレームの表示
    _player.previousFrame();
    break;
  case OF_KEY_RIGHT:
    // 次のフレームの表示
    _player.nextFrame();
    break;
  }
}

続いてキーボードの'o'を押した時にファイルダイアログを開いてGUIから動画ファイルを選択出来るようにしてみましょう。

void ofApp::setup(){
  ofBackground(255,255,255);
  ofSetVerticalSync(true);
  //_player.loadMovie("movies/fingers.mov");
  //_player.play();
}

void ofApp::keyPressed(int key){
  switch(key){
  case 'f':
    _player.setPaused(!_player.isPaused());
    break;
  case OF_KEY_LEFT:
    _player.previousFrame();
    break;
  case OF_KEY_RIGHT:
    _player.nextFrame();
    break;
  case 'o':
    {
      ofFileDialogResult openFileResult= ofSystemLoadDialog("Select a movie file");
      std::string file = openFileResult.getPath();
      if(!_player.loadMovie(file)){
        return;
      }
      _player.play();
    }
  }
}

このようにすると、'o'キーを押すことでファイル選択ダイアログが開き動画ファイルを指定する事が出来ます。
しかしファイル名が日本語の場合は以下のようなエラーが発生して読み込む事が出来ません。

[ error ] ofQuickTimePlayer: createMovoeFromPath(): couldn't load movie, OpenMov
ieFile failed: OSErr -50

デバッグして調べてみるとofSystemUtil.cppで文字化けが発生してしまっているようです。
仕方ないので こちらのページを参考に書き換えてしまいましょう。

ofSystemUtil.cppを以下のように書き換えます。

//ワイド文字列からマルチバイト文字列
//ロケール依存
static void narrow(const std::wstring &src, std::string &dest) {
  setlocale(LC_CTYPE, "");
  char *mbs = new char[src.length() * MB_CUR_MAX + 1];
  wcstombs(mbs, src.c_str(), src.length() * MB_CUR_MAX + 1);
  dest = mbs;
  delete [] mbs;
}

std::string
convertWideToNarrow( const wchar_t *s, char dfault = '?',
                     const std::locale& loc = std::locale() )
{
#if 0
  std::ostringstream stm;
  while( *s != L'\0' ) {
    stm << std::use_facet< std::ctype<wchar_t> >( loc ).narrow( *s++, dfault );
  }
  return stm.str();
#else
  std::wstring wstr(s);
  std::string ret;
  narrow(wstr, ret);
  return ret;
#endif
}

4. opencvの設定

次に動画から切り出したフレームをopencvで加工するための設定を行います。
openFrameworksにはopencvのラッパーであるofxCvというアドオンが存在しますが、内部で使用されているopencvのバージョンが古いのでそのままは使いたくありません。
Utilities.h/cppの2ファイルのみを拝借する事とし、別途最新のopencv(今回は2.4.9)を準備しましょう。

以下の2ファイルをプロジェクトに追加します。

  • ofxCv\libs\ofxCv\include\Utilities.h
  • ofxCv\libs\ofxCv\src\Utilities.cpp

またopencvが使えるように設定を行います。
opencvはvisual studio2012のコンパイル済みのものがこちらから手に入るのでこれを利用すると便利です。

visual studioからopencvを使用する方法は検索するといくらでも出てくるので詳細は省略します。
追加のインクルードディレクトリと追加のライブラリディレクトリの設定を済ませたら、main.cppファイルの先頭辺りに以下を記述します。

#include <opencv2/core/version.hpp>
#ifdef _MSC_VER
#define CV_NERSION_NO CVAUX_STR(CV_VERSION_EPOCH) CVAUX_STR(CV_VERSION_MAJOR) CVAUX_STR(CV_VERSION_MINOR)
#ifdef _DEBUG
//Debugモードの場合
#define OPENCV_LIB(name) "opencv_" #name CV_NERSION_NO "d.lib"
#else
//Releaseモードの場合
#define OPENCV_LIB(name) "opencv_" #name CV_NERSION_NO ".lib"
#endif // NDEBUG
#endif // _MSC_VER

#pragma comment(lib, OPENCV_LIB(core))
#pragma comment(lib, OPENCV_LIB(imgproc))
#pragma comment(lib, OPENCV_LIB(highgui))

これでopencvを使う準備が整いました。

5. 動画のフレームを変換

ofApp.cppファイルを次のように変更します。

#include "ofxCv/Utilities.h"

void ofApp::update(){
  _player.update();
  if(_player.isFrameNew()){
    ofPixelsRef pix = _player.getPixelsRef();
    cv::Mat m = ofxCv::toCv(pix).clone();
    // opencvはBGRなので変換が必要
    cv::cvtColor(m, m, CV_RGB2BGR);
    // 処理の一例
    cv::flip(m, m, 0); // 水平軸で反転(垂直反転)
    // フレームを反転させた画像を表示
    cv::imshow("frame", m);
  }
}

これで動画の各フレームに対して自由に処理を行う事が出来るようになりました。

おまけ

コーデックの関係か、動画ファイルによっては再生出来ないものがありました。
調べてみるとofDirectShowPlayerというアドオンがあり、こちらはDirectShowの実装を使えるようなのでwindowsの場合は良いかもしれません。
使い方は簡単で

void ofApp::setup(){
  _player.setPlayer( ofPtr <ofBaseVideoPlayer>(new ofDirectShowPlayer()));
}

とするだけです。
ただし、releaseビルドの実行時に時々ハングアップする現象が発生しました。
という事で本格的に動画ファイルを扱いたい場合にはFFmpeg等を使った方が良さそうです。
ただ少ないコード量で手軽に動画のフレーム編集を行いたい場合には今回の方法で十分かと思います。

Beyond Interaction[改訂第2版] -クリエイティブ・コーディングのためのopenFrameworks実践ガイド

Beyond Interaction[改訂第2版] -クリエイティブ・コーディングのためのopenFrameworks実践ガイド

開発時に便利なUnityのTips

Unity

今週はUnity開発時のTipsをいくつか。

取り込んだ画像の画質が悪い

f:id:cflat-inc:20140905134439p:plain

Aniso Levelの引き上げることで、急な角度でテクスチャを見る場合にテクスチャの品質を向上させる。

ついでにpng等透過画像の場合はAlpha is TransparencyのチェックをONにする。 この項目をチェックすると透過付きのテクスチャの透過と非透過部分との境界に発生するノイズが低減される。

NGUIのレイアウト

f:id:cflat-inc:20140905134456p:plain

NGUIはバージョンアップ毎に仕様が変わって大変ですが。

最初はFlexibleになってたはず。

これをConstrainedに変更しておけば、多少画面サイズが変わってもある程度いい感じにスケーリングされる。 http://www.tasharen.com/forum/index.php?topic=6710.0

GoogleAnalytics連携

ここ記事を参考にunity3d-google-analyticsを採用するのであれば、多少改造する必要ある場合がありそうなので、それについてちらほら。

http://qiita.com/yimajo/items/0861a79e741cbd463179

イベントにカスタムディメンションを追加したければ、CGoogleAnalytics.csを下記のようにコード変更。

public void TrackEvent(string _category,string _action, string _label, int _value){
        WWWForm postParams = new WWWForm();

        AddDefaultFields(ref postParams);

        postParams.AddField("t","event");
        postParams.AddField("ec",_category);
        postParams.AddField("el",_label);
        postParams.AddField("ea",_action);
        postParams.AddField("ev",_value);
        postParams.AddField("cd1",m_customerId);

        MakeRequest(postParams);
}

複数シーンをまたぐならCGoogleAnalytics.csにこれを追加。後は最初のシーンのみにGameObjectをぶちこんでおけばよい。

void Awake()
{
    DontDestroyOnLoad(this);
}

Unity4入門   最新開発環境による簡単3Dゲーム製作

Unity4入門 最新開発環境による簡単3Dゲーム製作

GreaseMonkeyのGM_setValue()の値はどこに?

GreaseMonkey

以前もご紹介した GreaseMonkey にて、GM_setValue() 関数がどこに値を保存するか、纏めてみました(Firefox のみ)。

以下、[@namespace]GreaseMonkey スクリプトのメタタグ@namespaceに設定した値、[@name]は同じく@nameに設定した値、[設定項目]GM_setValue("XXX", "YYY")XXXの部分、の意。
なお、これらの情報は今後の FireFoxGreaseMonkey のバージョンアップにより古くなる可能性があります。

GreaseMonkey ~1.12

試しに情報を調べようとすると、いまだに出てくるのがこれ。
現行バージョン(ver 2.2 現在)では使われていません。

  1. ロケーションバーに about:config を入力。警告が出ても華麗にスルーして設定一覧を表示。
  2. greasemonkey.scriptvals.[@namespace]/[@name].[設定項目]に保存されている。

なお、この値自体は FireFox のプロファイルフォルダ内に pref.js というファイルがあり、その中に書き込まれます。

GreaseMonkey 1.13~

プロファイルフォルダ内には gm_scripts というフォルダが存在します。
GreaseMonkey スクリプトはその中のフォルダに保存されていますが(基本的に、[@name] と一致)、それと同名の *.db ファイルが作られ、その中に保存されます。
SQLite 3 形式なので、SQLite Browser 等を用いて編集できます。

grant-none-shim.js を使う場合

GreaseMonkey 1.x では、GM_xxx()関数を使っている、@grantメタタグを設定していないスクリプトには、自動でその関数の利用権限が設定されていました。
GreaseMonkey 2.0 以降では、明示的に@grantメタタグを設定しなければ@grant noneとなり、GM_xxx()関数がundefinedとなるように変更されています。

grant-none-shim.js は、@grant noneを設定していても一部のGM_xxx()関数をエミュレートするようなスクリプトです。
このスクリプトはローカルストレージを利用してGM_setValue()をエミュレートしているので、保存先はプロファイルフォルダ内の webappsstore.sqlite ファイルとなります(これも SQLite 3 形式)。

  • scopeフィールド
    [Webサイトのドメイン名を反転させたもの].:[scheme]:[port]
    • [Webサイトのドメイン名を反転させたもの]は、例えば http://cflat-inc.hatenablog.com/ であれば "moc.golbanetah.cni-talfc" となります。
      たぶん、URL が後方から一致させてゆくものなので、最初から逆転させておくことで検索速度を向上させているのでしょう。
    • [scheme]は URL スキーム、すなわち "http" や "https" といった「URL の ":" より前の部分」です。
    • [port]はポート番号です。http であれば普通は 80 ですね。
  • keyフィールド
    • "***[@namespace]***[@name]***[設定項目]" のフォーマットです。
  • valueフィールド
    • GM_setValue()で設定した値が入ります。

Greasemonkeyスクリプティング TIPS&SAMPLES

Greasemonkeyスクリプティング TIPS&SAMPLES

fixedしたポインタにスコープを越えさせる

C#

 皆さん大好き*1unsafeコード。
 滅多にない事ですが、P/Invoke を使ってなんかいろいろと変な事をしていると、こんなコードを書きたくなってくる事があります:

class Hoge
{
    private byte[] m_ptr;

    public unsafe byte* GetPointer()
    {
        // &(m_ptr[0])を取得したい!
    }
}

 今回は、こんな時にどうするか、というお話。

やっちゃいけないパターン

 まず第一に思いつくのが、こんなコードです:

public unsafe byte* GetPointer()
{
    fixed (byte* ptr = m_ptr)
    {
        return ptr;
    }
}

 もちろん、こんな危険なコードを書いてはいけません。
 MSDN には、次のように書かれています:

ステートメントのコードを実行すると、固定された変数の固定が解除され、ガベージ コレクションの対象になります。 そのため、fixedステートメントの外部にある変数へのポインターは指定しないでください。

 ……わからん! この糞機械翻訳めが!

 というわけで原文がこちらです:

After the code in the statement is executed, any pinned variables are unpinned and subject to garbage collection. Therefore, do not point to those variables outside the fixed statement.

 これが意味するところを正確に訳すのであればこうでしょうか? 太字機械翻訳では不正確になっている部分です。

ステートメント内のコードを実行し終えた後、固定された変数の固定は解除され、ガベージ コレクションの対象になります。そのため、fixedステートメント外部では、これらの変数をポインタを介して使用してはいけません。

 つまり、上記のコードでは、こんな事が起こるかもしれないわけです:

byte *ptr = hoge.GetPointer();
// ここで GC が走る(かもしれない)
SomeNativeFunction(ptr); // GC が走った場合、ptr は無効な値を指しているため大変な事になる

正しい方法

 勘のいい方なら、きっとこう考えるに違いありません。
「確かlock(obj) { ... }ステートメントは、

System.Threading.Monitor.Enter(obj);
try {
    ...
}
finally {
    System.Threading.Monitor.Leave(obj);
}

シンタックスシュガーだったはずだぞ。ならばfixedも同様に何らかのメソッドで実現できるに違いない」

 もしもfixedの開始と終了をそれぞれ別のメソッドに分解できれば、fixedしたポインタを関数の外に取り出せます。そのような機能はあるのでしょうか?

 はい、あります。
 少しポインタの使い方が変わってしまいますが、System.Runtime.InteropServices.GCHandleというクラスがその機能を提供しています。

 GCHandleを使ってポインタを取り出す方法がこちらです:

class Hoge
{
    private byte[] m_ptr;

    public GCHandle GetPointerHandle()
    {
        return GCHandle.Alloc(m_ptr, GCHandleType.Pinned);
    }
}

GCHandle handle = hoge.GetPointerHandle();
SomeNativeFunction((byte*)handle.AddrOfPinnedObject().ToPointer());
handle.Free();

 GCHandle.Alloc()GCHandleType.Pinnedを渡すと、アドレス固定モードのGCHandleが返ります。
 次に、得たGCHandleに対してAddrOfPinnedObject()を呼ぶと、その固定されたアドレスがIntPtrとして返ります。
 最後にIntPtrToPointer()を呼ぶと、IntPtrvoid*になってくれるので、これを目的の型(今回はbyte*)にキャストしてやります。
 ここまででfixedステートメントの開始部分。

 fixedステートメントの終了部分に相当するのは、handle.Free()です。これを呼ばないとアドレスが固定されたままになり、ガベージコレクションの効率が悪化するため、忘れずに呼ぶようにして下さい。

でも本当は

 まあ……こんな事をするくらいなら、大抵の場合は最初からこれでいいんですけどね:

class Hoge
{
    private byte[] m_ptr;

    public byte[] GetArray()
    {
        return m_ptr;
    }
}

fixed (byte* ptr = hoge.GetArray()) {
    SomeNativeFunction(ptr);
}

 ですが実際は、設計の都合上これではいけない事も大いにあります。
 例えば、上記のコードでは、このような事ができてしまうでしょう:

byte[] arr = hoge.GetArray();
arr[0] = 10; // ← arr が参照なので、hoge の中身書き換えられてしまう!

 これを防ぐためにはGetArray()m_ptrをコピーしてから返せばよいのですが、場合によっては配列サイズが巨大なのでコピーさせたくない場合もあるかもしれません。
 そんな時、(良い設計とは言えませんが)敢えてGCHandleを返す事で、「お前ら、特別な場合(つまり API に渡して中で弄って貰う時)以外はこの戻り値を弄るんじゃないぞ」という意図を明確にする事ができるわけです。

 もう少しまともな設計にするのであれば、GCHandleのラッパーを作ってこうするでしょう:

sealed unsafe class SafePointer<T> : IDisposable
{
    private readonly GCHandle m_handle;
    private IntPtr m_ptr = IntPtr.Zero;
    private bool m_disposed = false;

    public SafePointer( T[] ptr )
    {
        if ( null != ptr ) {
            m_handle = GCHandle.Alloc( ptr, GCHandleType.Pinned );
            m_ptr = m_handle.AddrOfPinnedObject();
        }
    }

    ~SafePointer()
    {
        Dispose();
    }

    public bool IsDisposed
    {
        get { return m_disposed; }
    }

    public static implicit operator IntPtr( SafePointer<T> ptr )
    {
        return ptr.m_ptr;
    }

    public void Dispose()
    {
        if ( m_disposed ) return;

        m_disposed = true;

        if ( m_handle.IsAllocated ) {
            m_handle.Free();
            m_ptr = IntPtr.Zero;
        }
    }
}

 void*を返すか、IntPtrを返すかはお好みで構いませんが、いずれにせよ似たような形になる事は確かなのではないかと思います。
 なおもちろんですが、このクラスは破棄されるまでオブジェクトをピン止めし続け、GCの効率を下げるので、ご利用は計画的に。

[完全版] 究極のC#プログラミング ~新スタイルによる実践的コーディング

[完全版] 究極のC#プログラミング ~新スタイルによる実践的コーディング

*1:大好きなのはいいけれど多用されちゃ困るんですが。