SlideShare une entreprise Scribd logo
1  sur  104
Télécharger pour lire hors ligne
2012年12月24日
第1回勉強会:スライド


                1
注意!



      2
この発表は
ほぼ全編、黒魔術です!


              3
しかも、
あまり役に立たないことを
  解説していきます!


               4
なので、



       5
C言語初心者は、
  真に受けず、
聞き流してください!


             6
自己紹介

       名前:koturn 0;

 趣味:数学、プログラミング

      エディタ:vim派

 言語:C/C++/C#/Java/Python
/Ruby/JavaScript/common LISP
       /elisp/Vim script

                               7
アウトライン(1)
●   C言語の復習
    –   三項演算子
    –   カンマ演算子
    –   文字列の連結
●
    プリプロセスの復習
    –   プリプロセスとは?
    –   #include
    –   #define, #undef
    –   #if, #elif, #else, #endif
    –   #ifdef, #ifndef, #endif


                                    8
アウトライン(2)
●
    マクロテクニック
    –   文字列化演算子
    –   トークン連結演算子
    –   可変引数マクロ
●
    黒魔術
    –   printlnマクロ
    –   until文、unless文
    –   デバッグ用マクロ
    –   SWAPマクロ
    –   ダフのデバイス


                            9
アウトライン(1)
●   C言語の文法の復習
    –   三項演算子
    –   カンマ演算子
    –   文字列の連結
●
    プリプロセスの復習
    –   プリプロセスとは?
    –   #include
    –   #define, #undef
    –   #if, #elif, #else, #endif
    –   #ifdef, #ifndef, #endif


                                    10
三項演算子
                     復習
●
    以下のような形で記述されます
    <条件> ? <条件がtrueのときの値> : <条件がfalseのときの値>

●
    例:絶対値を求める三項演算子
    abs_value = x > 0 ? x : -x;

●   簡単な条件分岐であれば、if ~ elseより簡潔に
    記述可能です
●
    式でありながら、条件分岐できるのが強み!
    –   基本的に文を使えないマクロに適用しやすい!

                                              11
アウトライン(1)
●   C言語の文法の復習
    –   三項演算子
    –   カンマ演算子
    –   文字列の連結
●
    プリプロセスの復習
    –   プリプロセスとは?
    –   #include
    –   #define, #undef
    –   #if, #elif, #else, #endif
    –   #ifdef, #ifndef, #endif


                                    12
カンマ演算子
                      復習
●   カンマ演算子とは、複数の式を1つの式として
    扱う手段です
●   以下のように記述したとき、式全体の値は値n
    となります(各値は、左から1度ずつ評価され
    ます)
    (値1, 値2, 値3, … , 値n)

●   以下のように記述したとき、xに代入されるの
    は3となります
    x = (1, printf(“foo”), 2, printf(“bar”), 3);


●                                                  13
アウトライン(1)
●   C言語の文法の復習
    –   三項演算子
    –   カンマ演算子
    –   文字列の連結
●
    プリプロセスの復習
    –   プリプロセスとは?
    –   #include
    –   #define, #undef
    –   #if, #elif, #else, #endif
    –   #ifdef, #ifndef, #endif


                                    14
文字列の連結
                    復習
●   C言語では、ダブルクオートで囲んだ文字列
    を2つ続けて書くと、1つの文字列とみなされ
    ます
    // コンパイルエラーにならず、”Hello World”と表示
    printf(“Hello” “World!” “n”);

●   1回の関数呼び出しで、大量の文字列を渡すと
    きに便利です
    –   読みやすく書くことが出来ます
    puts(
       “usage:n”
       “[option]n”
       “ -o : output filen”
       “ -s : sentence file”
    );
                                       15
アウトライン(1)
●   C言語の文法の復習
    –   三項演算子
    –   カンマ演算子
    –   文字列の連結
●
    プリプロセスの復習
    –   プリプロセスとは?
    –   #include
    –   #define, #undef
    –   #if, #elif, #else, #endif
    –   #ifdef, #ifndef, #endif


                                    16
プリプロセスとは?
実行バイナリができるプロセス
●   実行バイナリが出来るまでの手順です
     Cソースコード
     Cソースコード
                プリプロセス
                プリプロセス

     Cプリプロセス
     Cプリプロセス
      ソースコード
      ソースコード    コンパイル
                コンパイル


     アセンブリコード
     アセンブリコード
                アセンブル
                アセンブル

      オブジェクト
      オブジェクト
       ファイル
       ファイル      リンク
                 リンク

       実行
       実行
      バイナリ
      バイナリ               17
プリプロセスとは?
          コマンド
●   具体的には、こういうコマンドを用います
       foo.c
        foo.c    $ gcc -E foo.c -o foo.i
                 $ gcc -E foo.c -o foo.i

       foo.i
        foo.i
                 $ gcc -S foo.i -o foo.s
                 $ gcc -S foo.i -o foo.s

       foo.s
        foo.s
                 $ gcc -c foo.s -o foo.o
                 $ gcc -c foo.s -o foo.o

       foo.o
        foo.o
                 $ gcc foo.o -o foo.out
                 $ gcc foo.o -o foo.out

      foo.out
       foo.out                             18
プリプロセスとは?
          概要
●   コンパイルする前の前処理です
●   やっていることは、単なるソースコードの置
    換です
●
    非常に貧弱な置換機能です
●   でも、うまく使えば、おいしい!
●   実は、C言語以外のソースコードにも適用可
    能です(Javaのソースコードなど)



                           19
アウトライン(1)
●   C言語の文法の復習
    –   三項演算子
    –   カンマ演算子
    –   文字列の連結
●
    プリプロセスの復習
    –   プリプロセスとは?
    –   #include
    –   #define, #undef
    –   #if, #elif, #else, #endif
    –   #ifdef, #ifndef, #endif


                                    20
#include
                          概要
●
    以下のような形で使用します
    #include <foo.h>   // インクルードパスの通ったところのヘッダ
    #include “bar.h”   // ユーザ定義のヘッダ
●   #define行は、指定ファイルの内容に置き換えら
    れます
●   <>は、インクルードパスの通ったところからの
    インクルードです(標準ライブラリ等)
●
    “”は、カレントディレクトリを基準にした、
    ユーザ定義のヘッダファイルのインクルードで
    す

                                                21
#include
              インクルードパス
●   インクルードパスとは?
    –   インクルードパスが通っているディレクトリな
        ら、<>でヘッダファイルを読み込めます
    –   標準ライブラリ(<stdio.h>、<stdlib.h>など)
●   インクルードパスを追加するには、コンパイ
    ラに、オプションとして与えます
●   例:gccの場合:-Iオプション
    $ gcc -I./hoge/fuga includetest.c -o includetest.out




                                                           22
アウトライン(1)
●   C言語の文法の復習
    –   三項演算子
    –   カンマ演算子
    –   文字列の連結
●
    プリプロセスの復習
    –   プリプロセスとは?
    –   #include
    –   #define, #undef
    –   #if, #elif, #else, #endif
    –   #ifdef, #ifndef, #endif


                                    23
#define, #undef
              概要
●   ソースコードの置き換えを定義します
●   一般的に、マクロと呼ばれる機構です
●   大きく分けて、3種類のマグロがあります




                           24
#define, #undef
                 概要
●   ソースコードの置き換えを定義します
●   一般的に、マクロと呼ばれる機構です
●   大きく分けて、3種類のマグロマクロがありま
    す
    –   キーワードマクロ
    –   定数マクロ
    –   関数マクロ



                              25
#define, #undef
              キーワードマクロ
●   キーワードのみを定義します
●   主に、インクルードガードに用いられます
    #define KEY_WORD

●   キーワードマクロと同じ識別子を記述する
    と、その識別子は無くなります
●   次に述べる、定数マクロの定数部分が無いも
    のと考えてよいでしょう



                                 26
#define, #undef
                  定数マクロ
●   C言語における、よく知られた定数の宣言方
    法です
    #define CONSTANT   200

●   識別子自体の置き換えも可能ですが、その用
    途で使われることは少ないです
    #define koturn return    // koturn 0;が可能になります




                                                    27
#define, #undef
                   関数マクロ
●   マクロの醍醐味!
●   関数のように、引数を取り、ソースコードを
    置き換えられます
    // 絶対値を返す関数マクロ
    #define ABS(x) ((x) > 0 ? (x) : -(x))

●   使用例:
    abs_value = ABS(-3);

●   プリプロセス後、以下のように置換されます
    abs_value = ((-3) > 0 ? (-3) : -(-3));



                                             28
#define, #undef
        関数マクロ:注意事項(1)
●   引数は、置換後の中では、丸括弧で囲いま
    しょう
    –   これをしないと、以下のようなマクロでは、期待
        した結果が得られません
    #define SQ(x)    (x * x)   // 2乗を行うマクロのつもり

●   使用例:
    a = SQ(3 + 5);    // aが64になることを期待


●   展開結果:
    a = (3 + 5 * 3 + 5);    // aは23になる


                                                 29
#define, #undef
       関数マクロ:注意事項(1)
●   2乗するマクロは以下のように書くべきです
    #define SQ(x)    ((x) * (x))   // 2乗を行うマクロ

●
    使用例:
    a = SQ(3 + 5);    // aが64になることを期待


●   展開結果:
    a = ((3 + 5) * (3 + 5));    // aは64になる




                                                 30
#define, #undef
        関数マクロ:注意事項(2)
●   引数が複数回評価されることによる副作用は
    避けられません
    #define SQ(x)   ((x) * (x))   // 2乗を行うマクロ

●   使用例:
    int n = 5;
    a = SQ(++n);    // nが6になり、aが36になることを期待

●   展開結果:
    –   インクリメントがどのタイミングで評価されるか
        は、未定義動作です
    a = ((++n) * (++n));   // nは7になり、aは49になる


                                                31
#define, #undef
        関数マクロ:注意事項(3)
●   引数の型をチェックすることができません
    –   この理由や、複数回評価の副作用があるため、C+
        +では、インライン関数を用いるのがよいです
●   しかし、関数マクロにしかできないこともあ
    ります!




                                  32
#undef
                         概要
●   #undefは、#defineで定義したマクロを無効化
    します
●   キーワードマクロ、定数マクロ、関数マクロ
    のいずれにも有効です
●   以下のようにして用います
    #define KEY_WORD
    #define CONSTANT 200
    #define SQ(x) ((x) * (x))

    #undef KEY_WORD   // この行以降は、KEY_WORDキーワードマクロは使えない
    #undef CONSTANT   // この行以降は、CONSTANT定数マクロは使えない
    #undef SQ         // この行以降は、SQ関数マクロは使えない



                                                        33
アウトライン(1)
●   C言語の文法の復習
    –   三項演算子
    –   カンマ演算子
    –   文字列の連結
●
    プリプロセスの復習
    –   プリプロセスとは?
    –   #include
    –   #define, #undef
    –   #if, #elif, #else, #endif
    –   #ifdef, #ifndef, #endif


                                    34
#if, #elif, #else, #endif
                     概要
●   条件コンパイルに用います
    –   同じソースコードでも、複数のOSやコンパイラに
        対応できます
●   宣言されているマクロに対するif ~ else文と
    いったところです
●   次のような形で書きます
    #if <条件1>
    // 何か書く
    #elif <条件2>   // 省略可
    // 何か書く
    #else         // 省略可
    // 何か書く
    #endif
                                       35
#if, #elif, #else, #endif
                     概要
●   条件部には、数値比較やマクロが定義されて
    いるかどうかを確かめるdefined演算子を用い
    ることができます
●   論理演算子も利用可能です
    // __STDC_VERSION__は定義済みマクロ
    #if __STDC_VERSION__ >= 199901 // C99規格でコンパイルするとき
    # define rep(i, n) for (int i = 0; i < (n); i++)
    #endif

    // FOOが定義されていて、かつBARが定義されていない、またはBAZが100以下なら
    #if defined(FOO) && !defined(BAR) || BAZ < 100
    # include “hoge.h”
    #else
    # include “piyo.h”
    #endif

                                                        36
#if, #elif, #else, #endif
             条件コンパイルの例
●   複数のOSに対応する例として、sleep()関数を
    取り上げましょう
    –   sleep()関数は<unistd.h>で宣言されています
    –   Windowsに<unistd.h>はありません
        ●   <Windows.h>に、似た機能のSleep()関数があります
    // Windowsでコンパイルするとき
    #if defined(WIN32) || defined(_WIN32)             
          || defined(__WIN32) || defined(__WIN32__)   
          || defined(WIN64)   || defined(_WIN64)      
          || defined(__WIN64) || defined(__WIN64__)
    # include <Windows.h>
    # define sleep(sec) Sleep(sec * 1000)

    // Windows以外でコンパイルするとき
    #else
    # include <unistd.h>                                  37
    #endif
アウトライン(1)
●   C言語の文法の復習
    –   三項演算子
    –   カンマ演算子
    –   文字列の連結
●
    プリプロセスの復習
    –   プリプロセスとは?
    –   #include
    –   #define, #undef
    –   #if, #elif, #else, #endif
    –   #ifdef, #ifndef, #endif


                                    38
#ifdef, #ifndef, #endif
                   概要
●   #ifdefは、#if defined()の省略形です
●   #ifndefは、#if !defined()の省略形です
●
    簡単に用いることができるが、論理演算子を
    用いることが出来ません
●   単に、マクロが定義されているかどうかを確
    認するときに用います
    –   インクルードガードなど
    #ifndef FOO_H
    #define FOO_H
    // ヘッダの内容を書いていく
    #endif

                                    39
余談
            改行、#if 0 ~ #endif
●   プリプロセスの指定は、行末に『』を入れる
    ことで、改行して続けて書くことが可能です
    // FOO, BAR, BAZがいずれも10より大きいならば、
    #if FOO > 10      
          && BAR > 10 
          && BAZ > 10 

●   #if 0 ~ #endifは、必ずプリプロセス段階で消え
    るので、複数行のコメントアウトとして使用
    可能です
    –   入れ子にすることも可能だったり
    #if 0
    printf(“debug:a = %dn”, a);
    #if 0
    printf(“debug:b = %dn”, b);
    #endif                             40
    #endif
アウトライン(2)
●
    マクロテクニック
    –   文字列化演算子
    –   トークン連結演算子
    –   可変引数マクロ
●
    黒魔術
    –   printlnマクロ
    –   until文、unless文
    –   デバッグ用マクロ
    –   SWAPマクロ
    –   ダフのデバイス


                            41
文字列化演算子
                  概要:#
●   関数マクロの引数を、ダブルクオートで囲
    い、文字列化する機能です
●   置換後の引数の左に、#を1つつけて記述しま
    す
    #define STR(x)   #x

●   使用例:
    printf(STR(1a2b3c));


●   展開結果
    printf(“1a2b3c”);


                            42
文字列化演算子
                 応用例:#
●   使いどころが難しいですが、文字列の結合と
    組み合わせるとよいでしょう
●   例:
    #define TRACE(var, fmt)   printf(#var “ = ” fmt “n”, var)


●   使用例と展開結果:
    int a = 10;
    TRACE(a, “%d”); // マクロの呼び出し
    printf(“a” “ = “ “%d” “n”, var); // このように展開される
    printf(“a = %dn”, var); // コンパイル後は、これと等価になる




                                                                 43
アウトライン(2)
●
    マクロテクニック
    –   文字列化演算子
    –   トークン連結演算子
    –   可変引数マクロ
●
    黒魔術
    –   printlnマクロ
    –   until文、unless文
    –   デバッグ用マクロ
    –   SWAPマクロ
    –   ダフのデバイス


                            44
トークン連結演算子
               概要:##
●
    置換後のマクロのトークンとトークンと結合
    させる演算子です
●
    主に、関数マクロの引数に用いられます
●
    例:
    #define GENVAR(num)   int var##num


●
    使用例:
    GENVAR(1) = 10;
    GENVAR(2) = 20;

●
    展開結果:
    int var1 = 10;
    int var2 = 20;
                                         45
トークン連結演算子
              応用例:##
●   うまく使うと、記述量を劇的に減らすことが
    できます!
●   例:構造体の再帰代入演算
●   Before:
    typedef struct {
      int x; int y; int z;
    } point;

    int main(void) {
      point p = { 2,   3, -2};
      point q = {-4,   1, 6};
      p.x += q.x;
      p.y += q.y;
      p.z += q.z;
      return 0;
    }
                                 46
トークン連結演算子
              応用例:##
●   うまく使うと、記述量を劇的に減らすことが
    できます!
●   例:構造体の再帰代入演算
●   After:
    typedef struct {
      int x; int y; int z;
    } point;
    #define REC_ASSIGN(op, point1, point2)   
    (                                        
      point1.x op##= point2.x,               
      point1.y op##= point2.y,               
      point1.z op##= point2.z                
    )
    int main(void) {
      point p = { 2, 3, -2};
      point q = {-4, 1, 6};
      REC_ASSIGN(+, p, q);
      return 0;                                  47
    }
アウトライン(2)
●
    マクロテクニック
    –   文字列化演算子
    –   トークン連結演算子
    –   可変引数マクロ
●
    黒魔術
    –   printlnマクロ
    –   until文、unless文
    –   デバッグ用マクロ
    –   SWAPマクロ
    –   ダフのデバイス


                            48
可変引数マクロ
                  概要
●
    関数マクロの引数を、可変にしたいことっ
    て、ありますよね?
●
    実はできます!
●
    具体的には、以下のように記述します
    #define err_printf(...)   fprintf(stderr, __VA_ARGS__)

●
    使用例:
    err_printf(“a[%d] = %d”, i, a[i]);

●
    展開結果:
    fprintf(stderr, “a[%d] = %d”, i, a[i]);

                                                             49
可変引数マクロ
                  注意点
●   可変引数マクロに与える引数を0個にする場合
    には、注意が必要です
●
    例:
    #define dbg_printf(fmt, ...)                
      fprintf(stderr, “%s at line %u : ” fmt,   
        __FILE__, __LINE__, __VA_ARGS__)


●
    使用例:
    dbg_printf(“Hello World”);


●
    展開結果:
    // 可変引数を省略すると、カンマが残る
    fprintf(stderr, “%s at line %u : “ “Hello World”,
      “foo.c”, 25, );                                   50
可変引数マクロ
            省略可能にするには
●   ##__VA_ARGS__と記述すると、省略可能にな
    ります
●
    例:
    #define dbg_printf(fmt, ...)                
      fprintf(stderr, “%s at line %u : ” fmt,   
        __FILE__, __LINE__, ##__VA_ARGS__)


●
    使用例:
    dbg_printf(“Hello World”);


●
    展開結果:
    // 今度は、カンマが残っていない
    fprintf(stderr, “%s at line %u : “ “Hello World”,
      “foo.c”, 25);                                     51
アウトライン(2)
●
    マクロテクニック
    –   文字列化演算子
    –   トークン連結演算子
    –   可変引数マクロ
●
    黒魔術
    –   printlnマクロ
    –   until文、unless文
    –   デバッグ用マクロ
    –   SWAPマクロ
    –   ダフのデバイス


                            52
printlnマクロ
                 意義
●   printf()関数って、わざわざnをつけるのが面倒
    ですよね?
●   JavaのSystem.out.println()メソッドみたいに、
    自動で改行をつけて欲しいですよね?




                                         53
printlnマクロ
                 意義
●   printf()関数って、わざわざnをつけるのが面倒
    ですよね?
●   JavaのSystem.out.println()メソッドみたいに、
    自動で改行をつけて欲しいですよね?

●   はい、できます!
●   そう、関数マクロならね



                                         54
printlnマクロ
                    マクロ定義
●   改行つきのprintf()関数のマクロです
    #define println(fmt, ...)    printf(fmt “n”, ##__VA_ARGS__)

●
    使用例:
    println(“a = %d”, 10);

●   展開結果:
    printf(“a = %d” “n”, 10);    // a = 10と表示し、改行される




                                                                   55
アウトライン(2)
●
    マクロテクニック
    –   文字列化演算子
    –   トークン連結演算子
    –   可変引数マクロ
●
    黒魔術
    –   printlnマクロ
    –   until文、unless文
    –   デバッグ用マクロ
    –   SWAPマクロ
    –   ダフのデバイス


                            56
unless文、until文
               意義
●   Rubyには、until文、unless文がありますよね?
●   C言語でも欲しくないですか?




                                    57
unless文、until文
               意義
●   Rubyには、until文、unless文がありますよね?
●   C言語でも欲しくないですか?

●   ええ、できますとも!
●   そう、関数マクロならね




                                    58
unless文、until文
                  マクロ定義
●   カンマ演算子を利用されることを想定して、
    可変引数を用います
●   最後の値が、式の値になることを利用すれ
    ば、以下のように定義できますね!
    #define unless(...)   if(!(__VA_ARGS__))
    #define until(...)    while(!(__VA_ARGS__))




                                                  59
アウトライン(2)
●
    マクロテクニック
    –   文字列化演算子
    –   トークン連結演算子
    –   可変引数マクロ
●
    黒魔術
    –   printlnマクロ
    –   until文、unless文
    –   デバッグ用マクロ
    –   SWAPマクロ
    –   ダフのデバイス


                            60
デバッグ用マクロ
                 意義
●   デバッグ時のみ、有効にするコードを書くの
    は面倒ですよね?
●   以下のような#ifdef ~ #endifのコードを散在さ
    せるのは嫌ですよね?
    –   2行もプリプロセス行で増えてしまいます!
    #ifdef DEBUG
    fprintf(stderr, “This is debug coden”);
    #endif




                                               61
デバッグ用マクロ
                 意義
●   デバッグ時のみ、有効にするコードを書くの
    は面倒ですよね?
●   以下のような#ifdef ~ #endifのコードを散在さ
    せるのは嫌ですよね?
    –   2行もプリプロセス行で増えてしまいます!
    #ifdef DEBUG
    fprintf(stderr, “This is debug coden”);
    #endif


●   そんなときは、やっぱり関数マクロ!

                                               62
デバッグ用マクロ
                マクロ定義
●
    以下のようにすると、デバッグ時のみ有効になる
    dbg_printf()関数を定義できます
    #ifdef DEBUG
    # define dbg_printf(...) fprintf(stderr, __VA_ARGS__)
    #else
    # define dbg_printf (1 ? (void) 0 : fprintf)
    #endif


●   DEBUGマクロが定義されていないときは、コンパ
    イラの最適化で、コードが消えます!
●   0をvoidキャストしているのは、ワーニングを消す
    ためです

                                                            63
アウトライン(2)
●
    マクロテクニック
    –   文字列化演算子
    –   トークン連結演算子
    –   可変引数マクロ
●
    黒魔術
    –   printlnマクロ
    –   until文、unless文
    –   デバッグ用マクロ
    –   SWAPマクロ
    –   ダフのデバイス


                            64
SWAPマクロ
                      意義
●   2数を交換する関数は、以下のようになります
    void swap(int *a, int *b) {
      int tmp = *a;
      *a = *b;
      *b = tmp
    }

●   使用例:
    int a = 10, b = 20;
    swap(&a, &b);

●   こんな単純な関数を、呼び出すのはもったい
    ないですね


                                  65
SWAPマクロ
                      意義
●   2数を交換する関数は、以下のようになります
    void swap(int *a, int *b) {
      int tmp = *a;
      *a = *b;
      *b = tmp
    }
●
    使用例:
    int a = 10, b = 20;
    swap(&a, &b);

●
    こんな単純な関数を、呼び出すのはもったいな
    いですね
●
    マクロで書きましょう!

                                  66
SWAPマクロ
                     基本形
●   一番オーソドックスなSWAPマクロです
    #define SWAP(type, a, b)         
    {                                
      type __tmp_swap_var__ = (a);   
      (a) = (b);                     
      (b) = __tmp_swap_var__;        
    }


●   使用例:
    int a = 10, b = 20;
    SWAP(int, a, b);




                                         67
SWAPマクロ
               基本形:問題点
●   関数呼び出しのように使えません
    –   関数呼び出しだと、アドレスを渡す必要がありま
        した
●   複文に置き換えられるので、セミコロンを記
    述する必要がないです
    –   でも、SWAP(int, a, b);と書いても問題ないです
●   以下のような、中括弧省略のif ~ else節におい
    て問題があります
    // コンパイルエラーとなる
    if (a == 10)
      SWAP(int, a, b);
    else
      SWAP(int, a, c);
                                         68
SWAPマクロ
                基本形:解決策
●   先程の問題点から、シームレスな(関数を呼び出す
    ような)SWAPマクロの使用はできませんでした
●   以下のように改良します
    –   ポインタ型を引数に取るように作る
        ●
            マクロであることがよりシームレスになります
        ●   最適化に貢献(明らかに同一の変数のSWAPは行わなくなる)
    –   do ~ while(0)で囲う
        ●
            呼び出し側に、セミコロンをつけることを強制します
        ●   中括弧省略型if ~ elseでもエラーが出なくなります
        ●   コンパイラの最適化により、do ~ while(0)の判定は消えます
●



                                                 69
SWAPマクロ
                  基本形:改善
●   以下が改善したものとなります
    #define SWAP(type, a, b)          
    do {                              
      type __tmp_swap_var__ = *(a);   
      *(a) = *(b);                    
      *(b) = __tmp_swap_var__;        
    } while (0)


●   型を指定する必要があるので、完全にシーム
    レスとは言い切れませんが...。
●   使用例:
    int a = 10, b = 20;
    SWAP(int, &a, &b);


                                          70
SWAPマクロ
      よりシームレスにするには
●   よりシームレスなSWAPマクロを目指したい!
●   完成形としては、以下のような呼び出しが可
    能であればよいですね
    SWAP(&a, &b);


●   引数の型指定を無くすには・・・。




                             71
SWAPマクロ
        よりシームレスにするには
●   よりシームレスなSWAPマクロを目指したい!
●   完成形としては、以下のような呼び出しが可
    能であればよいですね
    SWAP(&a, &b);


●   引数の型指定を無くすには・・・。
    –   xorを用いたSWAP
    –   加減算を用いたSWAP
    –   GNU拡張文法を用いたSWAP

                             72
SWAPマクロ
                    xor SWAP
●   xor(排他的論理和)を用いたSWAPアルゴリズムです
    #define SWAP(a, b)   
    (                    
      *(a) ^= *(b),      
      *(b) ^= *(a),      
      *(a) ^= *(b)       
    )
●
    変数宣言の必要がないため、カンマ演算子で連結するこ
    とにより、置換後全体を式化しています
●
    よりカッコよく書くと↓
    –   ※コンパイラの警告レベルを上げると、警告が出ます
    #define SWAP(a, b)   (*(a) ^= *(b) ^= *(a) ^= *(b))




                                                          73
SWAPマクロ
          xor SWAP:問題点
●   同一の変数に対して用いた場合、変数の値が0
    になります
●   整数の変数に対してのみ、使用可能です
    –   浮動小数点数と構造体には使用不可
●   スピードは、一時変数を用いるものより遅い
    です




                            74
SWAPマクロ
          xor SWAP:問題点
●   同一の変数に対して用いた場合、変数の値が0
    になります
●   整数の変数に対してのみ、使用可能です
    –   浮動小数点数と構造体には使用不可
●   スピードは、一時変数を用いるものより遅い
    です
                解決可能!


                            75
SWAPマクロ
           xor SWAP:安全版(1)
●
    同一の変数かどうかをチェックするために、三項演
    算子を組み込みます
    #define SWAP(a, b)    
    (((a) == (b)) ? 0 :   
    (                     
       *(a) ^= *(b),      
       *(b) ^= *(a),      
       *(a) ^= *(b),      
       1                  
    ))

●
    条件判断が加わる分、処理が増えますが・・・
    –   aとbのアドレスがコンパイル段階で異なると分かるとき
        は、最適化により条件判断部分は消えます

                                     76
SWAPマクロ
           xor SWAP:安全版(2)
●   短絡評価を用いると以下のように、カッコよ
    く書けます!
    #define SWAP(a, b) 
      (((a) != (b)) && (*(a) ^= *(b) ^= *(a) ^= *(b), 1))




     カッコイイ!



                                                            77
SWAPマクロ
                   加減算SWAP
●   足し算と引き算を用いたSWAPアルゴリズムです
    #define SWAP(a, b)       
    (                        
      *(a) += *(b),          
      *(b) = *(a) - *(b),    
      *(a) -= *(b)           
    )
●
    変数宣言の必要がないため、カンマ演算子で連結するこ
    とにより、置換後全体を式化しています
●
    よりカッコよく書くと↓
    –   ※コンパイラの警告レベルを上げると、警告が出ます
    #define SWAP(a, b)   (*(a) += *(b) -= *(a) = *(b) - *(a))




                                                                78
SWAPマクロ
         加減算SWAP:問題点
●   同一の変数に対して用いた場合、変数の値が0
    になります
●   整数と浮動小数点の変数に対してのみ、使用
    可能です
    –   構造体には使用不可
●   浮動小数点数に用いた場合、丸め誤差が発生
    する場合があります
●   スピードは、一時変数を用いるものやxorを用
    いるものよりも遅いです

                             79
SWAPマクロ
         加減算SWAP:問題点
●   同一の変数に対して用いた場合、変数の値が0
    になります
●   整数と浮動小数点の変数に対してのみ、使用
    可能です
    –   構造体には使用不可   解決可能!
●   浮動小数点数に用いた場合、丸め誤差が発生
    する場合があります
●   スピードは、一時変数を用いるものやxorを用
    いるものよりも遅いです

                             80
SWAPマクロ
         加減算SWAP:安全版(1)
●
    同一の変数かどうかをチェックするために、三項演
    算子を組み込みます
    #define SWAP(a, b)       
    (((a) == (b)) ? 0 :      
    (                        
       *(a) += *(b),         
       *(b) = *(a) - *(b),   
       *(a) -= *(b),         
       1                     
    ))

●
    条件判断が加わる分、処理が増えますが・・・
    –   aとbのアドレスがコンパイル段階で異なると分かるとき
        は、最適化により条件判断部分は消えます

                                     81
SWAPマクロ
        加減算SWAP:安全版(2)
●   短絡評価を用いると以下のように、カッコよ
    く書けます!
    #define SWAP(a, b) 
      (((a) != (b)) && (*(a) += *(b) -= *(a) = *(b) - *(a), 1))




     カッコイイ!!



                                                                  82
SWAPマクロ
            GNU拡張文法SWAP
●   gccのC言語拡張文法を使用したマクロです
    –   typeof演算子
    –   複文の式化
●   シームレスかつ性能もいいです
●   最強のSWAPマクロ?
    #define SWAP(a, b)                         
    ({                                         
       typeof(*(a)) __tmp_swap_var__ = *(a);   
       *(a) = *(b);                            
       *(b) = __tmp_swap_var__;                
    })



                                                   83
アウトライン(2)
●
    マクロテクニック
    –   文字列化演算子
    –   トークン連結演算子
    –   可変引数マクロ
●
    黒魔術
    –   printlnマクロ
    –   until文、unless文
    –   デバッグ用マクロ
    –   SWAPマクロ
    –   ダフのデバイス


                            84
ダフのデバイス
               概要
●   Tom Duffという人が考案しました
●   ループのアンロールに用いられます
    –   ループの条件判断回数を減らすことにより、高速
        化を目指しています
●   switch文のfall-throughを利用しています
●   caseラベルをまたがるdo ~ while文が特徴です
    –   C言語のswitch文は、規定が弱いから利用可能
    –   JavaやC#のswitch文では、利用不可


                                   85
ダフのデバイス
                 コード(1)
●   ダフのデバイスによる、配列のコピーを行う
    関数です
    void duff_memcpy(int *to, const int *from, int size) {
      int n = (size + 7) >> 3;
      switch (size & 7) {
        case 0: do { *to++ = *from++;
        case 7:      *to++ = *from++;
        case 6:      *to++ = *from++;
        case 5:      *to++ = *from++;
        case 4:      *to++ = *from++;
        case 3:      *to++ = *from++;
        case 2:      *to++ = *from++;
        case 1:      *to++ = *from++;
                } while (--n);
      }
    }



                                                             86
ダフのデバイス
                 コード(2)
●   次のように書くこともできます
    void duff_memcpy(int *to, const int *from, int size) {
      switch (size & 7) {
        case 0: do { *to++ = *from++;
        case 7:      *to++ = *from++;
        case 6:      *to++ = *from++;
        case 5:      *to++ = *from++;
        case 4:      *to++ = *from++;
        case 3:      *to++ = *from++;
        case 2:      *to++ = *from++;
        case 1:      *to++ = *from++;
                } while (size -= 8);
      }
    }




                                                             87
ダフのデバイス
          特徴
●   今回取り上げた例では、8回毎のアンロール処
    理でしたが、もっと回数を増やすことも可能
    です
●   現代では、あまり大きく性能向上に寄与しま
    せん(アンロール回数を256回毎程度にする
    と、良くなりますが・・・)
●   コード量(つまり、実行バイナリのサイズ)が
    少し増えます


                            88
ダフのデバイス
          要望
●   メモリコピー以外に応用したい!
●   同じ処理を記述してくのはめんどくさい!




                          89
ダフのデバイス
          要望
●   メモリコピー以外に応用したい!
●   同じ処理を記述してくのはめんどくさい!

●   ええ、解決できますとも!
●   そう、関数マクロならね




                          90
ダフのデバイス
            関数マクロへの応用
●   以下の<処理>の部分に、行いたい処理が来る
    とよいですよね
    void duff_memcpy(int *to, const int *from, int size) {
      int n = (size + 7) >> 3;
      switch (size & 7) {
        case 0: do { <処理>;
        case 7:      <処理>;
        case 6:      <処理>;
        case 5:      <処理>;
        case 4:      <処理>;
        case 3:      <処理>;
        case 2:      <処理>;
        case 1:      <処理>;
                } while (--n);
      }
    }


                                                             91
ダフのデバイス
            関数マクロへの応用
●   ダフのデバイスをマクロ化すると、以下のよ
    うになります
    –   STATEMENT:行いたい処理(式または複文)
    –   __VA_ARGS__:次のステップ前の処理(省略化)
    #define DUFFS_LOOP(n, STATEMENT, ...)               
    {                                                   
      register int __tmp_loop_var__ = ((n) + 7) >> 3;   
      switch ((n) & 7) {                                
        case 0: do { STATEMENT; __VA_ARGS__;            
        case 7:      STATEMENT; __VA_ARGS__;            
        case 6:      STATEMENT; __VA_ARGS__;            
        case 5:      STATEMENT; __VA_ARGS__;            
        case 4:      STATEMENT; __VA_ARGS__;            
        case 3:      STATEMENT; __VA_ARGS__;            
        case 2:      STATEMENT; __VA_ARGS__;            
        case 1:      STATEMENT; __VA_ARGS__;            
                } while (--__tmp_loop_var__);           
      }                                                    92
    }
ダフのデバイス
            関数マクロへの応用
●   使用例:
    int a = 0;
    DUFFS_LOOP(10, {
      printf(“a = %dn”, a);
    }, a++);

●   複文が引数となるので、呼び出し側からは、
    奇妙な形に見えますね




                               93
参考文献
●   プログラミング言語C 第2版




                     94
参考文献
●
    トリッキーコードネット
    –   http://tricky-code.net/
●   苦しんで覚えるC言語
    –   http://9cguide.appspot.com/
●   C言語の小技:SWAPマクロ
    –   http://no1-bug-creator.cocolog-nifty.com/blog/2011/08/cswap-2ccd.html
●   Duff's device
    –   http://ja.wikipedia.org/wiki/Duff's_device
●   ダフのデバイスというのを初めて知った - 星一の日記
    –   http://d.hatena.ne.jp/hajimehoshi/20080428/1209314296


                                                                                95
以上で、
発表終了です!


          96
どうでしたか?



          97
実際のコーディングの
  役に立たない
   知識ばかり
   でしたね?

             98
でも、



      99
ベテランの書いた
C言語ソースを読むときに
   役に立つかも
    しれません

               100
また、
  面白トリビアとして
楽しんでいただけたのなら
     幸いです

               101
そして何より、
   関数マクロを
書く助けになったのなら
   嬉しいです!

              102
Let,s Enjoy
Meta-Programming!


                    103
御清聴
 ありがとう
ございました!
  m(__)m

           104

Contenu connexe

Tendances

C++入門?
C++入門?C++入門?
C++入門?tsudaa
 
C++ tips 3 カンマ演算子編
C++ tips 3 カンマ演算子編C++ tips 3 カンマ演算子編
C++ tips 3 カンマ演算子編道化師 堂華
 
組み込みでこそC++を使う10の理由
組み込みでこそC++を使う10の理由組み込みでこそC++を使う10の理由
組み込みでこそC++を使う10の理由kikairoya
 
知って得するC#
知って得するC#知って得するC#
知って得するC#Shota Baba
 
C++ tips2 インクリメント編
C++ tips2 インクリメント編C++ tips2 インクリメント編
C++ tips2 インクリメント編道化師 堂華
 
JSX / Haxe / TypeScript
JSX / Haxe / TypeScriptJSX / Haxe / TypeScript
JSX / Haxe / TypeScriptbleis tift
 
プログラムの処方箋~健康なコードと病んだコード
プログラムの処方箋~健康なコードと病んだコードプログラムの処方箋~健康なコードと病んだコード
プログラムの処方箋~健康なコードと病んだコードShigenori Sagawa
 
C++ Template Meta Programming の紹介@社内勉強会
C++ Template Meta Programming の紹介@社内勉強会C++ Template Meta Programming の紹介@社内勉強会
C++ Template Meta Programming の紹介@社内勉強会Akihiko Matuura
 
C# 8.0 null許容参照型
C# 8.0 null許容参照型C# 8.0 null許容参照型
C# 8.0 null許容参照型信之 岩永
 
Visual C++で使えるC++11
Visual C++で使えるC++11Visual C++で使えるC++11
Visual C++で使えるC++11nekko1119
 
クロージャデザインパターン
クロージャデザインパターンクロージャデザインパターン
クロージャデザインパターンMoriharu Ohzu
 
ゲーム開発者のための C++11/C++14
ゲーム開発者のための C++11/C++14ゲーム開発者のための C++11/C++14
ゲーム開発者のための C++11/C++14Ryo Suzuki
 
オブジェクト指向できていますか?
オブジェクト指向できていますか?オブジェクト指向できていますか?
オブジェクト指向できていますか?Moriharu Ohzu
 
C#や.NET Frameworkがやっていること
C#や.NET FrameworkがやっていることC#や.NET Frameworkがやっていること
C#や.NET Frameworkがやっていること信之 岩永
 
わんくま同盟大阪勉強会#61
わんくま同盟大阪勉強会#61わんくま同盟大阪勉強会#61
わんくま同盟大阪勉強会#61TATSUYA HAYAMIZU
 
templateとautoの型推論
templateとautoの型推論templateとautoの型推論
templateとautoの型推論MITSUNARI Shigeo
 

Tendances (20)

C++入門?
C++入門?C++入門?
C++入門?
 
C++ tips1 #include編
C++ tips1 #include編C++ tips1 #include編
C++ tips1 #include編
 
C++ tips 3 カンマ演算子編
C++ tips 3 カンマ演算子編C++ tips 3 カンマ演算子編
C++ tips 3 カンマ演算子編
 
組み込みでこそC++を使う10の理由
組み込みでこそC++を使う10の理由組み込みでこそC++を使う10の理由
組み込みでこそC++を使う10の理由
 
知って得するC#
知って得するC#知って得するC#
知って得するC#
 
C++ tips2 インクリメント編
C++ tips2 インクリメント編C++ tips2 インクリメント編
C++ tips2 インクリメント編
 
JSX / Haxe / TypeScript
JSX / Haxe / TypeScriptJSX / Haxe / TypeScript
JSX / Haxe / TypeScript
 
プログラムの処方箋~健康なコードと病んだコード
プログラムの処方箋~健康なコードと病んだコードプログラムの処方箋~健康なコードと病んだコード
プログラムの処方箋~健康なコードと病んだコード
 
C++ Template Meta Programming の紹介@社内勉強会
C++ Template Meta Programming の紹介@社内勉強会C++ Template Meta Programming の紹介@社内勉強会
C++ Template Meta Programming の紹介@社内勉強会
 
C# 8.0 null許容参照型
C# 8.0 null許容参照型C# 8.0 null許容参照型
C# 8.0 null許容参照型
 
Visual C++で使えるC++11
Visual C++で使えるC++11Visual C++で使えるC++11
Visual C++で使えるC++11
 
クロージャデザインパターン
クロージャデザインパターンクロージャデザインパターン
クロージャデザインパターン
 
Emcpp item31
Emcpp item31Emcpp item31
Emcpp item31
 
C++14 Overview
C++14 OverviewC++14 Overview
C++14 Overview
 
ゲーム開発者のための C++11/C++14
ゲーム開発者のための C++11/C++14ゲーム開発者のための C++11/C++14
ゲーム開発者のための C++11/C++14
 
オブジェクト指向できていますか?
オブジェクト指向できていますか?オブジェクト指向できていますか?
オブジェクト指向できていますか?
 
C#や.NET Frameworkがやっていること
C#や.NET FrameworkがやっていることC#や.NET Frameworkがやっていること
C#や.NET Frameworkがやっていること
 
わんくま同盟大阪勉強会#61
わんくま同盟大阪勉強会#61わんくま同盟大阪勉強会#61
わんくま同盟大阪勉強会#61
 
C# 9.0 / .NET 5.0
C# 9.0 / .NET 5.0C# 9.0 / .NET 5.0
C# 9.0 / .NET 5.0
 
templateとautoの型推論
templateとautoの型推論templateとautoの型推論
templateとautoの型推論
 

Similaire à 第1回勉強会スライド

関数型言語&形式的手法セミナー(3)
関数型言語&形式的手法セミナー(3)関数型言語&形式的手法セミナー(3)
関数型言語&形式的手法セミナー(3)啓 小笠原
 
Lightning introduction to CoffeeScript 20131005
Lightning introduction to CoffeeScript 20131005Lightning introduction to CoffeeScript 20131005
Lightning introduction to CoffeeScript 20131005gotohayato
 
Define and expansion of cpp macro
Define and expansion of cpp macroDefine and expansion of cpp macro
Define and expansion of cpp macrodigitalghost
 
C・C++用のコードカバレッジツールを自作してみた話
C・C++用のコードカバレッジツールを自作してみた話C・C++用のコードカバレッジツールを自作してみた話
C・C++用のコードカバレッジツールを自作してみた話simotin13 Miyazaki
 
【学習メモ#4th】12ステップで作る組込みOS自作入門
【学習メモ#4th】12ステップで作る組込みOS自作入門【学習メモ#4th】12ステップで作る組込みOS自作入門
【学習メモ#4th】12ステップで作る組込みOS自作入門sandai
 
プログラミング言語 Ruby 2章 Rubyプログラムの構造と実行
プログラミング言語 Ruby 2章 Rubyプログラムの構造と実行プログラミング言語 Ruby 2章 Rubyプログラムの構造と実行
プログラミング言語 Ruby 2章 Rubyプログラムの構造と実行monglee
 
言語処理系入門€10
言語処理系入門€10言語処理系入門€10
言語処理系入門€10Kenta Hattori
 
Lisp Tutorial for Pythonista Day 6
Lisp Tutorial for Pythonista Day 6Lisp Tutorial for Pythonista Day 6
Lisp Tutorial for Pythonista Day 6Ransui Iso
 
20130228 Goノススメ(BPStudy #66)
20130228 Goノススメ(BPStudy #66)20130228 Goノススメ(BPStudy #66)
20130228 Goノススメ(BPStudy #66)Yoshifumi Yamaguchi
 
Debug Hacks at Security and Programming camp 2011
Debug Hacks at Security and Programming camp 2011 Debug Hacks at Security and Programming camp 2011
Debug Hacks at Security and Programming camp 2011 Hiro Yoshioka
 
Unity2015_No10_~UGUI&Audio~
Unity2015_No10_~UGUI&Audio~Unity2015_No10_~UGUI&Audio~
Unity2015_No10_~UGUI&Audio~CHY72
 
Programming camp code reading
Programming camp code readingProgramming camp code reading
Programming camp code readingHiro Yoshioka
 
Code Reading at Security and Programming camp 2011
Code Reading at Security and Programming camp 2011 Code Reading at Security and Programming camp 2011
Code Reading at Security and Programming camp 2011 Hiro Yoshioka
 
Lisp batton - Common LISP
Lisp batton - Common LISPLisp batton - Common LISP
Lisp batton - Common LISPMasaomi CHIBA
 
Programming camp 2010 debug hacks
Programming camp 2010 debug hacksProgramming camp 2010 debug hacks
Programming camp 2010 debug hacksHiro Yoshioka
 
90分 Scheme to C(勝手に抄訳版)
90分 Scheme to C(勝手に抄訳版)90分 Scheme to C(勝手に抄訳版)
90分 Scheme to C(勝手に抄訳版)ryos36
 

Similaire à 第1回勉強会スライド (20)

関数型言語&形式的手法セミナー(3)
関数型言語&形式的手法セミナー(3)関数型言語&形式的手法セミナー(3)
関数型言語&形式的手法セミナー(3)
 
Lightning introduction to CoffeeScript 20131005
Lightning introduction to CoffeeScript 20131005Lightning introduction to CoffeeScript 20131005
Lightning introduction to CoffeeScript 20131005
 
Define and expansion of cpp macro
Define and expansion of cpp macroDefine and expansion of cpp macro
Define and expansion of cpp macro
 
C・C++用のコードカバレッジツールを自作してみた話
C・C++用のコードカバレッジツールを自作してみた話C・C++用のコードカバレッジツールを自作してみた話
C・C++用のコードカバレッジツールを自作してみた話
 
【学習メモ#4th】12ステップで作る組込みOS自作入門
【学習メモ#4th】12ステップで作る組込みOS自作入門【学習メモ#4th】12ステップで作る組込みOS自作入門
【学習メモ#4th】12ステップで作る組込みOS自作入門
 
プログラミング言語 Ruby 2章 Rubyプログラムの構造と実行
プログラミング言語 Ruby 2章 Rubyプログラムの構造と実行プログラミング言語 Ruby 2章 Rubyプログラムの構造と実行
プログラミング言語 Ruby 2章 Rubyプログラムの構造と実行
 
言語処理系入門€10
言語処理系入門€10言語処理系入門€10
言語処理系入門€10
 
C#勉強会
C#勉強会C#勉強会
C#勉強会
 
Lisp Tutorial for Pythonista Day 6
Lisp Tutorial for Pythonista Day 6Lisp Tutorial for Pythonista Day 6
Lisp Tutorial for Pythonista Day 6
 
Coqでsprintf
CoqでsprintfCoqでsprintf
Coqでsprintf
 
Unix
UnixUnix
Unix
 
Coqでsprintf
CoqでsprintfCoqでsprintf
Coqでsprintf
 
20130228 Goノススメ(BPStudy #66)
20130228 Goノススメ(BPStudy #66)20130228 Goノススメ(BPStudy #66)
20130228 Goノススメ(BPStudy #66)
 
Debug Hacks at Security and Programming camp 2011
Debug Hacks at Security and Programming camp 2011 Debug Hacks at Security and Programming camp 2011
Debug Hacks at Security and Programming camp 2011
 
Unity2015_No10_~UGUI&Audio~
Unity2015_No10_~UGUI&Audio~Unity2015_No10_~UGUI&Audio~
Unity2015_No10_~UGUI&Audio~
 
Programming camp code reading
Programming camp code readingProgramming camp code reading
Programming camp code reading
 
Code Reading at Security and Programming camp 2011
Code Reading at Security and Programming camp 2011 Code Reading at Security and Programming camp 2011
Code Reading at Security and Programming camp 2011
 
Lisp batton - Common LISP
Lisp batton - Common LISPLisp batton - Common LISP
Lisp batton - Common LISP
 
Programming camp 2010 debug hacks
Programming camp 2010 debug hacksProgramming camp 2010 debug hacks
Programming camp 2010 debug hacks
 
90分 Scheme to C(勝手に抄訳版)
90分 Scheme to C(勝手に抄訳版)90分 Scheme to C(勝手に抄訳版)
90分 Scheme to C(勝手に抄訳版)
 

Dernier

CTO, VPoE, テックリードなどリーダーポジションに登用したくなるのはどんな人材か?
CTO, VPoE, テックリードなどリーダーポジションに登用したくなるのはどんな人材か?CTO, VPoE, テックリードなどリーダーポジションに登用したくなるのはどんな人材か?
CTO, VPoE, テックリードなどリーダーポジションに登用したくなるのはどんな人材か?akihisamiyanaga1
 
TataPixel: 畳の異方性を利用した切り替え可能なディスプレイの提案
TataPixel: 畳の異方性を利用した切り替え可能なディスプレイの提案TataPixel: 畳の異方性を利用した切り替え可能なディスプレイの提案
TataPixel: 畳の異方性を利用した切り替え可能なディスプレイの提案sugiuralab
 
AWS の OpenShift サービス (ROSA) を使った OpenShift Virtualizationの始め方.pdf
AWS の OpenShift サービス (ROSA) を使った OpenShift Virtualizationの始め方.pdfAWS の OpenShift サービス (ROSA) を使った OpenShift Virtualizationの始め方.pdf
AWS の OpenShift サービス (ROSA) を使った OpenShift Virtualizationの始め方.pdfFumieNakayama
 
クラウドネイティブなサーバー仮想化基盤 - OpenShift Virtualization.pdf
クラウドネイティブなサーバー仮想化基盤 - OpenShift Virtualization.pdfクラウドネイティブなサーバー仮想化基盤 - OpenShift Virtualization.pdf
クラウドネイティブなサーバー仮想化基盤 - OpenShift Virtualization.pdfFumieNakayama
 
自分史上一番早い2024振り返り〜コロナ後、仕事は通常ペースに戻ったか〜 by IoT fullstack engineer
自分史上一番早い2024振り返り〜コロナ後、仕事は通常ペースに戻ったか〜 by IoT fullstack engineer自分史上一番早い2024振り返り〜コロナ後、仕事は通常ペースに戻ったか〜 by IoT fullstack engineer
自分史上一番早い2024振り返り〜コロナ後、仕事は通常ペースに戻ったか〜 by IoT fullstack engineerYuki Kikuchi
 
NewSQLの可用性構成パターン(OCHaCafe Season 8 #4 発表資料)
NewSQLの可用性構成パターン(OCHaCafe Season 8 #4 発表資料)NewSQLの可用性構成パターン(OCHaCafe Season 8 #4 発表資料)
NewSQLの可用性構成パターン(OCHaCafe Season 8 #4 発表資料)NTT DATA Technology & Innovation
 
業務で生成AIを活用したい人のための生成AI入門講座(社外公開版:キンドリルジャパン社内勉強会:2024年4月発表)
業務で生成AIを活用したい人のための生成AI入門講座(社外公開版:キンドリルジャパン社内勉強会:2024年4月発表)業務で生成AIを活用したい人のための生成AI入門講座(社外公開版:キンドリルジャパン社内勉強会:2024年4月発表)
業務で生成AIを活用したい人のための生成AI入門講座(社外公開版:キンドリルジャパン社内勉強会:2024年4月発表)Hiroshi Tomioka
 
デジタル・フォレンジックの最新動向(2024年4月27日情洛会総会特別講演スライド)
デジタル・フォレンジックの最新動向(2024年4月27日情洛会総会特別講演スライド)デジタル・フォレンジックの最新動向(2024年4月27日情洛会総会特別講演スライド)
デジタル・フォレンジックの最新動向(2024年4月27日情洛会総会特別講演スライド)UEHARA, Tetsutaro
 
モーダル間の変換後の一致性とジャンル表を用いた解釈可能性の考察 ~Text-to-MusicとText-To-ImageかつImage-to-Music...
モーダル間の変換後の一致性とジャンル表を用いた解釈可能性の考察  ~Text-to-MusicとText-To-ImageかつImage-to-Music...モーダル間の変換後の一致性とジャンル表を用いた解釈可能性の考察  ~Text-to-MusicとText-To-ImageかつImage-to-Music...
モーダル間の変換後の一致性とジャンル表を用いた解釈可能性の考察 ~Text-to-MusicとText-To-ImageかつImage-to-Music...博三 太田
 

Dernier (9)

CTO, VPoE, テックリードなどリーダーポジションに登用したくなるのはどんな人材か?
CTO, VPoE, テックリードなどリーダーポジションに登用したくなるのはどんな人材か?CTO, VPoE, テックリードなどリーダーポジションに登用したくなるのはどんな人材か?
CTO, VPoE, テックリードなどリーダーポジションに登用したくなるのはどんな人材か?
 
TataPixel: 畳の異方性を利用した切り替え可能なディスプレイの提案
TataPixel: 畳の異方性を利用した切り替え可能なディスプレイの提案TataPixel: 畳の異方性を利用した切り替え可能なディスプレイの提案
TataPixel: 畳の異方性を利用した切り替え可能なディスプレイの提案
 
AWS の OpenShift サービス (ROSA) を使った OpenShift Virtualizationの始め方.pdf
AWS の OpenShift サービス (ROSA) を使った OpenShift Virtualizationの始め方.pdfAWS の OpenShift サービス (ROSA) を使った OpenShift Virtualizationの始め方.pdf
AWS の OpenShift サービス (ROSA) を使った OpenShift Virtualizationの始め方.pdf
 
クラウドネイティブなサーバー仮想化基盤 - OpenShift Virtualization.pdf
クラウドネイティブなサーバー仮想化基盤 - OpenShift Virtualization.pdfクラウドネイティブなサーバー仮想化基盤 - OpenShift Virtualization.pdf
クラウドネイティブなサーバー仮想化基盤 - OpenShift Virtualization.pdf
 
自分史上一番早い2024振り返り〜コロナ後、仕事は通常ペースに戻ったか〜 by IoT fullstack engineer
自分史上一番早い2024振り返り〜コロナ後、仕事は通常ペースに戻ったか〜 by IoT fullstack engineer自分史上一番早い2024振り返り〜コロナ後、仕事は通常ペースに戻ったか〜 by IoT fullstack engineer
自分史上一番早い2024振り返り〜コロナ後、仕事は通常ペースに戻ったか〜 by IoT fullstack engineer
 
NewSQLの可用性構成パターン(OCHaCafe Season 8 #4 発表資料)
NewSQLの可用性構成パターン(OCHaCafe Season 8 #4 発表資料)NewSQLの可用性構成パターン(OCHaCafe Season 8 #4 発表資料)
NewSQLの可用性構成パターン(OCHaCafe Season 8 #4 発表資料)
 
業務で生成AIを活用したい人のための生成AI入門講座(社外公開版:キンドリルジャパン社内勉強会:2024年4月発表)
業務で生成AIを活用したい人のための生成AI入門講座(社外公開版:キンドリルジャパン社内勉強会:2024年4月発表)業務で生成AIを活用したい人のための生成AI入門講座(社外公開版:キンドリルジャパン社内勉強会:2024年4月発表)
業務で生成AIを活用したい人のための生成AI入門講座(社外公開版:キンドリルジャパン社内勉強会:2024年4月発表)
 
デジタル・フォレンジックの最新動向(2024年4月27日情洛会総会特別講演スライド)
デジタル・フォレンジックの最新動向(2024年4月27日情洛会総会特別講演スライド)デジタル・フォレンジックの最新動向(2024年4月27日情洛会総会特別講演スライド)
デジタル・フォレンジックの最新動向(2024年4月27日情洛会総会特別講演スライド)
 
モーダル間の変換後の一致性とジャンル表を用いた解釈可能性の考察 ~Text-to-MusicとText-To-ImageかつImage-to-Music...
モーダル間の変換後の一致性とジャンル表を用いた解釈可能性の考察  ~Text-to-MusicとText-To-ImageかつImage-to-Music...モーダル間の変換後の一致性とジャンル表を用いた解釈可能性の考察  ~Text-to-MusicとText-To-ImageかつImage-to-Music...
モーダル間の変換後の一致性とジャンル表を用いた解釈可能性の考察 ~Text-to-MusicとText-To-ImageかつImage-to-Music...
 

第1回勉強会スライド

  • 7. 自己紹介 名前:koturn 0; 趣味:数学、プログラミング エディタ:vim派 言語:C/C++/C#/Java/Python /Ruby/JavaScript/common LISP /elisp/Vim script 7
  • 8. アウトライン(1) ● C言語の復習 – 三項演算子 – カンマ演算子 – 文字列の連結 ● プリプロセスの復習 – プリプロセスとは? – #include – #define, #undef – #if, #elif, #else, #endif – #ifdef, #ifndef, #endif 8
  • 9. アウトライン(2) ● マクロテクニック – 文字列化演算子 – トークン連結演算子 – 可変引数マクロ ● 黒魔術 – printlnマクロ – until文、unless文 – デバッグ用マクロ – SWAPマクロ – ダフのデバイス 9
  • 10. アウトライン(1) ● C言語の文法の復習 – 三項演算子 – カンマ演算子 – 文字列の連結 ● プリプロセスの復習 – プリプロセスとは? – #include – #define, #undef – #if, #elif, #else, #endif – #ifdef, #ifndef, #endif 10
  • 11. 三項演算子 復習 ● 以下のような形で記述されます <条件> ? <条件がtrueのときの値> : <条件がfalseのときの値> ● 例:絶対値を求める三項演算子 abs_value = x > 0 ? x : -x; ● 簡単な条件分岐であれば、if ~ elseより簡潔に 記述可能です ● 式でありながら、条件分岐できるのが強み! – 基本的に文を使えないマクロに適用しやすい! 11
  • 12. アウトライン(1) ● C言語の文法の復習 – 三項演算子 – カンマ演算子 – 文字列の連結 ● プリプロセスの復習 – プリプロセスとは? – #include – #define, #undef – #if, #elif, #else, #endif – #ifdef, #ifndef, #endif 12
  • 13. カンマ演算子 復習 ● カンマ演算子とは、複数の式を1つの式として 扱う手段です ● 以下のように記述したとき、式全体の値は値n となります(各値は、左から1度ずつ評価され ます) (値1, 値2, 値3, … , 値n) ● 以下のように記述したとき、xに代入されるの は3となります x = (1, printf(“foo”), 2, printf(“bar”), 3); ● 13
  • 14. アウトライン(1) ● C言語の文法の復習 – 三項演算子 – カンマ演算子 – 文字列の連結 ● プリプロセスの復習 – プリプロセスとは? – #include – #define, #undef – #if, #elif, #else, #endif – #ifdef, #ifndef, #endif 14
  • 15. 文字列の連結 復習 ● C言語では、ダブルクオートで囲んだ文字列 を2つ続けて書くと、1つの文字列とみなされ ます // コンパイルエラーにならず、”Hello World”と表示 printf(“Hello” “World!” “n”); ● 1回の関数呼び出しで、大量の文字列を渡すと きに便利です – 読みやすく書くことが出来ます puts( “usage:n” “[option]n” “ -o : output filen” “ -s : sentence file” ); 15
  • 16. アウトライン(1) ● C言語の文法の復習 – 三項演算子 – カンマ演算子 – 文字列の連結 ● プリプロセスの復習 – プリプロセスとは? – #include – #define, #undef – #if, #elif, #else, #endif – #ifdef, #ifndef, #endif 16
  • 17. プリプロセスとは? 実行バイナリができるプロセス ● 実行バイナリが出来るまでの手順です Cソースコード Cソースコード プリプロセス プリプロセス Cプリプロセス Cプリプロセス ソースコード ソースコード コンパイル コンパイル アセンブリコード アセンブリコード アセンブル アセンブル オブジェクト オブジェクト ファイル ファイル リンク リンク 実行 実行 バイナリ バイナリ 17
  • 18. プリプロセスとは? コマンド ● 具体的には、こういうコマンドを用います foo.c foo.c $ gcc -E foo.c -o foo.i $ gcc -E foo.c -o foo.i foo.i foo.i $ gcc -S foo.i -o foo.s $ gcc -S foo.i -o foo.s foo.s foo.s $ gcc -c foo.s -o foo.o $ gcc -c foo.s -o foo.o foo.o foo.o $ gcc foo.o -o foo.out $ gcc foo.o -o foo.out foo.out foo.out 18
  • 19. プリプロセスとは? 概要 ● コンパイルする前の前処理です ● やっていることは、単なるソースコードの置 換です ● 非常に貧弱な置換機能です ● でも、うまく使えば、おいしい! ● 実は、C言語以外のソースコードにも適用可 能です(Javaのソースコードなど) 19
  • 20. アウトライン(1) ● C言語の文法の復習 – 三項演算子 – カンマ演算子 – 文字列の連結 ● プリプロセスの復習 – プリプロセスとは? – #include – #define, #undef – #if, #elif, #else, #endif – #ifdef, #ifndef, #endif 20
  • 21. #include 概要 ● 以下のような形で使用します #include <foo.h> // インクルードパスの通ったところのヘッダ #include “bar.h” // ユーザ定義のヘッダ ● #define行は、指定ファイルの内容に置き換えら れます ● <>は、インクルードパスの通ったところからの インクルードです(標準ライブラリ等) ● “”は、カレントディレクトリを基準にした、 ユーザ定義のヘッダファイルのインクルードで す 21
  • 22. #include インクルードパス ● インクルードパスとは? – インクルードパスが通っているディレクトリな ら、<>でヘッダファイルを読み込めます – 標準ライブラリ(<stdio.h>、<stdlib.h>など) ● インクルードパスを追加するには、コンパイ ラに、オプションとして与えます ● 例:gccの場合:-Iオプション $ gcc -I./hoge/fuga includetest.c -o includetest.out 22
  • 23. アウトライン(1) ● C言語の文法の復習 – 三項演算子 – カンマ演算子 – 文字列の連結 ● プリプロセスの復習 – プリプロセスとは? – #include – #define, #undef – #if, #elif, #else, #endif – #ifdef, #ifndef, #endif 23
  • 24. #define, #undef 概要 ● ソースコードの置き換えを定義します ● 一般的に、マクロと呼ばれる機構です ● 大きく分けて、3種類のマグロがあります 24
  • 25. #define, #undef 概要 ● ソースコードの置き換えを定義します ● 一般的に、マクロと呼ばれる機構です ● 大きく分けて、3種類のマグロマクロがありま す – キーワードマクロ – 定数マクロ – 関数マクロ 25
  • 26. #define, #undef キーワードマクロ ● キーワードのみを定義します ● 主に、インクルードガードに用いられます #define KEY_WORD ● キーワードマクロと同じ識別子を記述する と、その識別子は無くなります ● 次に述べる、定数マクロの定数部分が無いも のと考えてよいでしょう 26
  • 27. #define, #undef 定数マクロ ● C言語における、よく知られた定数の宣言方 法です #define CONSTANT 200 ● 識別子自体の置き換えも可能ですが、その用 途で使われることは少ないです #define koturn return // koturn 0;が可能になります 27
  • 28. #define, #undef 関数マクロ ● マクロの醍醐味! ● 関数のように、引数を取り、ソースコードを 置き換えられます // 絶対値を返す関数マクロ #define ABS(x) ((x) > 0 ? (x) : -(x)) ● 使用例: abs_value = ABS(-3); ● プリプロセス後、以下のように置換されます abs_value = ((-3) > 0 ? (-3) : -(-3)); 28
  • 29. #define, #undef 関数マクロ:注意事項(1) ● 引数は、置換後の中では、丸括弧で囲いま しょう – これをしないと、以下のようなマクロでは、期待 した結果が得られません #define SQ(x) (x * x) // 2乗を行うマクロのつもり ● 使用例: a = SQ(3 + 5); // aが64になることを期待 ● 展開結果: a = (3 + 5 * 3 + 5); // aは23になる 29
  • 30. #define, #undef 関数マクロ:注意事項(1) ● 2乗するマクロは以下のように書くべきです #define SQ(x) ((x) * (x)) // 2乗を行うマクロ ● 使用例: a = SQ(3 + 5); // aが64になることを期待 ● 展開結果: a = ((3 + 5) * (3 + 5)); // aは64になる 30
  • 31. #define, #undef 関数マクロ:注意事項(2) ● 引数が複数回評価されることによる副作用は 避けられません #define SQ(x) ((x) * (x)) // 2乗を行うマクロ ● 使用例: int n = 5; a = SQ(++n); // nが6になり、aが36になることを期待 ● 展開結果: – インクリメントがどのタイミングで評価されるか は、未定義動作です a = ((++n) * (++n)); // nは7になり、aは49になる 31
  • 32. #define, #undef 関数マクロ:注意事項(3) ● 引数の型をチェックすることができません – この理由や、複数回評価の副作用があるため、C+ +では、インライン関数を用いるのがよいです ● しかし、関数マクロにしかできないこともあ ります! 32
  • 33. #undef 概要 ● #undefは、#defineで定義したマクロを無効化 します ● キーワードマクロ、定数マクロ、関数マクロ のいずれにも有効です ● 以下のようにして用います #define KEY_WORD #define CONSTANT 200 #define SQ(x) ((x) * (x)) #undef KEY_WORD // この行以降は、KEY_WORDキーワードマクロは使えない #undef CONSTANT // この行以降は、CONSTANT定数マクロは使えない #undef SQ // この行以降は、SQ関数マクロは使えない 33
  • 34. アウトライン(1) ● C言語の文法の復習 – 三項演算子 – カンマ演算子 – 文字列の連結 ● プリプロセスの復習 – プリプロセスとは? – #include – #define, #undef – #if, #elif, #else, #endif – #ifdef, #ifndef, #endif 34
  • 35. #if, #elif, #else, #endif 概要 ● 条件コンパイルに用います – 同じソースコードでも、複数のOSやコンパイラに 対応できます ● 宣言されているマクロに対するif ~ else文と いったところです ● 次のような形で書きます #if <条件1> // 何か書く #elif <条件2> // 省略可 // 何か書く #else // 省略可 // 何か書く #endif 35
  • 36. #if, #elif, #else, #endif 概要 ● 条件部には、数値比較やマクロが定義されて いるかどうかを確かめるdefined演算子を用い ることができます ● 論理演算子も利用可能です // __STDC_VERSION__は定義済みマクロ #if __STDC_VERSION__ >= 199901 // C99規格でコンパイルするとき # define rep(i, n) for (int i = 0; i < (n); i++) #endif // FOOが定義されていて、かつBARが定義されていない、またはBAZが100以下なら #if defined(FOO) && !defined(BAR) || BAZ < 100 # include “hoge.h” #else # include “piyo.h” #endif 36
  • 37. #if, #elif, #else, #endif 条件コンパイルの例 ● 複数のOSに対応する例として、sleep()関数を 取り上げましょう – sleep()関数は<unistd.h>で宣言されています – Windowsに<unistd.h>はありません ● <Windows.h>に、似た機能のSleep()関数があります // Windowsでコンパイルするとき #if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) # include <Windows.h> # define sleep(sec) Sleep(sec * 1000) // Windows以外でコンパイルするとき #else # include <unistd.h> 37 #endif
  • 38. アウトライン(1) ● C言語の文法の復習 – 三項演算子 – カンマ演算子 – 文字列の連結 ● プリプロセスの復習 – プリプロセスとは? – #include – #define, #undef – #if, #elif, #else, #endif – #ifdef, #ifndef, #endif 38
  • 39. #ifdef, #ifndef, #endif 概要 ● #ifdefは、#if defined()の省略形です ● #ifndefは、#if !defined()の省略形です ● 簡単に用いることができるが、論理演算子を 用いることが出来ません ● 単に、マクロが定義されているかどうかを確 認するときに用います – インクルードガードなど #ifndef FOO_H #define FOO_H // ヘッダの内容を書いていく #endif 39
  • 40. 余談 改行、#if 0 ~ #endif ● プリプロセスの指定は、行末に『』を入れる ことで、改行して続けて書くことが可能です // FOO, BAR, BAZがいずれも10より大きいならば、 #if FOO > 10 && BAR > 10 && BAZ > 10 ● #if 0 ~ #endifは、必ずプリプロセス段階で消え るので、複数行のコメントアウトとして使用 可能です – 入れ子にすることも可能だったり #if 0 printf(“debug:a = %dn”, a); #if 0 printf(“debug:b = %dn”, b); #endif 40 #endif
  • 41. アウトライン(2) ● マクロテクニック – 文字列化演算子 – トークン連結演算子 – 可変引数マクロ ● 黒魔術 – printlnマクロ – until文、unless文 – デバッグ用マクロ – SWAPマクロ – ダフのデバイス 41
  • 42. 文字列化演算子 概要:# ● 関数マクロの引数を、ダブルクオートで囲 い、文字列化する機能です ● 置換後の引数の左に、#を1つつけて記述しま す #define STR(x) #x ● 使用例: printf(STR(1a2b3c)); ● 展開結果 printf(“1a2b3c”); 42
  • 43. 文字列化演算子 応用例:# ● 使いどころが難しいですが、文字列の結合と 組み合わせるとよいでしょう ● 例: #define TRACE(var, fmt) printf(#var “ = ” fmt “n”, var) ● 使用例と展開結果: int a = 10; TRACE(a, “%d”); // マクロの呼び出し printf(“a” “ = “ “%d” “n”, var); // このように展開される printf(“a = %dn”, var); // コンパイル後は、これと等価になる 43
  • 44. アウトライン(2) ● マクロテクニック – 文字列化演算子 – トークン連結演算子 – 可変引数マクロ ● 黒魔術 – printlnマクロ – until文、unless文 – デバッグ用マクロ – SWAPマクロ – ダフのデバイス 44
  • 45. トークン連結演算子 概要:## ● 置換後のマクロのトークンとトークンと結合 させる演算子です ● 主に、関数マクロの引数に用いられます ● 例: #define GENVAR(num) int var##num ● 使用例: GENVAR(1) = 10; GENVAR(2) = 20; ● 展開結果: int var1 = 10; int var2 = 20; 45
  • 46. トークン連結演算子 応用例:## ● うまく使うと、記述量を劇的に減らすことが できます! ● 例:構造体の再帰代入演算 ● Before: typedef struct { int x; int y; int z; } point; int main(void) { point p = { 2, 3, -2}; point q = {-4, 1, 6}; p.x += q.x; p.y += q.y; p.z += q.z; return 0; } 46
  • 47. トークン連結演算子 応用例:## ● うまく使うと、記述量を劇的に減らすことが できます! ● 例:構造体の再帰代入演算 ● After: typedef struct { int x; int y; int z; } point; #define REC_ASSIGN(op, point1, point2) ( point1.x op##= point2.x, point1.y op##= point2.y, point1.z op##= point2.z ) int main(void) { point p = { 2, 3, -2}; point q = {-4, 1, 6}; REC_ASSIGN(+, p, q); return 0; 47 }
  • 48. アウトライン(2) ● マクロテクニック – 文字列化演算子 – トークン連結演算子 – 可変引数マクロ ● 黒魔術 – printlnマクロ – until文、unless文 – デバッグ用マクロ – SWAPマクロ – ダフのデバイス 48
  • 49. 可変引数マクロ 概要 ● 関数マクロの引数を、可変にしたいことっ て、ありますよね? ● 実はできます! ● 具体的には、以下のように記述します #define err_printf(...) fprintf(stderr, __VA_ARGS__) ● 使用例: err_printf(“a[%d] = %d”, i, a[i]); ● 展開結果: fprintf(stderr, “a[%d] = %d”, i, a[i]); 49
  • 50. 可変引数マクロ 注意点 ● 可変引数マクロに与える引数を0個にする場合 には、注意が必要です ● 例: #define dbg_printf(fmt, ...) fprintf(stderr, “%s at line %u : ” fmt, __FILE__, __LINE__, __VA_ARGS__) ● 使用例: dbg_printf(“Hello World”); ● 展開結果: // 可変引数を省略すると、カンマが残る fprintf(stderr, “%s at line %u : “ “Hello World”, “foo.c”, 25, ); 50
  • 51. 可変引数マクロ 省略可能にするには ● ##__VA_ARGS__と記述すると、省略可能にな ります ● 例: #define dbg_printf(fmt, ...) fprintf(stderr, “%s at line %u : ” fmt, __FILE__, __LINE__, ##__VA_ARGS__) ● 使用例: dbg_printf(“Hello World”); ● 展開結果: // 今度は、カンマが残っていない fprintf(stderr, “%s at line %u : “ “Hello World”, “foo.c”, 25); 51
  • 52. アウトライン(2) ● マクロテクニック – 文字列化演算子 – トークン連結演算子 – 可変引数マクロ ● 黒魔術 – printlnマクロ – until文、unless文 – デバッグ用マクロ – SWAPマクロ – ダフのデバイス 52
  • 53. printlnマクロ 意義 ● printf()関数って、わざわざnをつけるのが面倒 ですよね? ● JavaのSystem.out.println()メソッドみたいに、 自動で改行をつけて欲しいですよね? 53
  • 54. printlnマクロ 意義 ● printf()関数って、わざわざnをつけるのが面倒 ですよね? ● JavaのSystem.out.println()メソッドみたいに、 自動で改行をつけて欲しいですよね? ● はい、できます! ● そう、関数マクロならね 54
  • 55. printlnマクロ マクロ定義 ● 改行つきのprintf()関数のマクロです #define println(fmt, ...) printf(fmt “n”, ##__VA_ARGS__) ● 使用例: println(“a = %d”, 10); ● 展開結果: printf(“a = %d” “n”, 10); // a = 10と表示し、改行される 55
  • 56. アウトライン(2) ● マクロテクニック – 文字列化演算子 – トークン連結演算子 – 可変引数マクロ ● 黒魔術 – printlnマクロ – until文、unless文 – デバッグ用マクロ – SWAPマクロ – ダフのデバイス 56
  • 57. unless文、until文 意義 ● Rubyには、until文、unless文がありますよね? ● C言語でも欲しくないですか? 57
  • 58. unless文、until文 意義 ● Rubyには、until文、unless文がありますよね? ● C言語でも欲しくないですか? ● ええ、できますとも! ● そう、関数マクロならね 58
  • 59. unless文、until文 マクロ定義 ● カンマ演算子を利用されることを想定して、 可変引数を用います ● 最後の値が、式の値になることを利用すれ ば、以下のように定義できますね! #define unless(...) if(!(__VA_ARGS__)) #define until(...) while(!(__VA_ARGS__)) 59
  • 60. アウトライン(2) ● マクロテクニック – 文字列化演算子 – トークン連結演算子 – 可変引数マクロ ● 黒魔術 – printlnマクロ – until文、unless文 – デバッグ用マクロ – SWAPマクロ – ダフのデバイス 60
  • 61. デバッグ用マクロ 意義 ● デバッグ時のみ、有効にするコードを書くの は面倒ですよね? ● 以下のような#ifdef ~ #endifのコードを散在さ せるのは嫌ですよね? – 2行もプリプロセス行で増えてしまいます! #ifdef DEBUG fprintf(stderr, “This is debug coden”); #endif 61
  • 62. デバッグ用マクロ 意義 ● デバッグ時のみ、有効にするコードを書くの は面倒ですよね? ● 以下のような#ifdef ~ #endifのコードを散在さ せるのは嫌ですよね? – 2行もプリプロセス行で増えてしまいます! #ifdef DEBUG fprintf(stderr, “This is debug coden”); #endif ● そんなときは、やっぱり関数マクロ! 62
  • 63. デバッグ用マクロ マクロ定義 ● 以下のようにすると、デバッグ時のみ有効になる dbg_printf()関数を定義できます #ifdef DEBUG # define dbg_printf(...) fprintf(stderr, __VA_ARGS__) #else # define dbg_printf (1 ? (void) 0 : fprintf) #endif ● DEBUGマクロが定義されていないときは、コンパ イラの最適化で、コードが消えます! ● 0をvoidキャストしているのは、ワーニングを消す ためです 63
  • 64. アウトライン(2) ● マクロテクニック – 文字列化演算子 – トークン連結演算子 – 可変引数マクロ ● 黒魔術 – printlnマクロ – until文、unless文 – デバッグ用マクロ – SWAPマクロ – ダフのデバイス 64
  • 65. SWAPマクロ 意義 ● 2数を交換する関数は、以下のようになります void swap(int *a, int *b) { int tmp = *a; *a = *b; *b = tmp } ● 使用例: int a = 10, b = 20; swap(&a, &b); ● こんな単純な関数を、呼び出すのはもったい ないですね 65
  • 66. SWAPマクロ 意義 ● 2数を交換する関数は、以下のようになります void swap(int *a, int *b) { int tmp = *a; *a = *b; *b = tmp } ● 使用例: int a = 10, b = 20; swap(&a, &b); ● こんな単純な関数を、呼び出すのはもったいな いですね ● マクロで書きましょう! 66
  • 67. SWAPマクロ 基本形 ● 一番オーソドックスなSWAPマクロです #define SWAP(type, a, b) { type __tmp_swap_var__ = (a); (a) = (b); (b) = __tmp_swap_var__; } ● 使用例: int a = 10, b = 20; SWAP(int, a, b); 67
  • 68. SWAPマクロ 基本形:問題点 ● 関数呼び出しのように使えません – 関数呼び出しだと、アドレスを渡す必要がありま した ● 複文に置き換えられるので、セミコロンを記 述する必要がないです – でも、SWAP(int, a, b);と書いても問題ないです ● 以下のような、中括弧省略のif ~ else節におい て問題があります // コンパイルエラーとなる if (a == 10) SWAP(int, a, b); else SWAP(int, a, c); 68
  • 69. SWAPマクロ 基本形:解決策 ● 先程の問題点から、シームレスな(関数を呼び出す ような)SWAPマクロの使用はできませんでした ● 以下のように改良します – ポインタ型を引数に取るように作る ● マクロであることがよりシームレスになります ● 最適化に貢献(明らかに同一の変数のSWAPは行わなくなる) – do ~ while(0)で囲う ● 呼び出し側に、セミコロンをつけることを強制します ● 中括弧省略型if ~ elseでもエラーが出なくなります ● コンパイラの最適化により、do ~ while(0)の判定は消えます ● 69
  • 70. SWAPマクロ 基本形:改善 ● 以下が改善したものとなります #define SWAP(type, a, b) do { type __tmp_swap_var__ = *(a); *(a) = *(b); *(b) = __tmp_swap_var__; } while (0) ● 型を指定する必要があるので、完全にシーム レスとは言い切れませんが...。 ● 使用例: int a = 10, b = 20; SWAP(int, &a, &b); 70
  • 71. SWAPマクロ よりシームレスにするには ● よりシームレスなSWAPマクロを目指したい! ● 完成形としては、以下のような呼び出しが可 能であればよいですね SWAP(&a, &b); ● 引数の型指定を無くすには・・・。 71
  • 72. SWAPマクロ よりシームレスにするには ● よりシームレスなSWAPマクロを目指したい! ● 完成形としては、以下のような呼び出しが可 能であればよいですね SWAP(&a, &b); ● 引数の型指定を無くすには・・・。 – xorを用いたSWAP – 加減算を用いたSWAP – GNU拡張文法を用いたSWAP 72
  • 73. SWAPマクロ xor SWAP ● xor(排他的論理和)を用いたSWAPアルゴリズムです #define SWAP(a, b) ( *(a) ^= *(b), *(b) ^= *(a), *(a) ^= *(b) ) ● 変数宣言の必要がないため、カンマ演算子で連結するこ とにより、置換後全体を式化しています ● よりカッコよく書くと↓ – ※コンパイラの警告レベルを上げると、警告が出ます #define SWAP(a, b) (*(a) ^= *(b) ^= *(a) ^= *(b)) 73
  • 74. SWAPマクロ xor SWAP:問題点 ● 同一の変数に対して用いた場合、変数の値が0 になります ● 整数の変数に対してのみ、使用可能です – 浮動小数点数と構造体には使用不可 ● スピードは、一時変数を用いるものより遅い です 74
  • 75. SWAPマクロ xor SWAP:問題点 ● 同一の変数に対して用いた場合、変数の値が0 になります ● 整数の変数に対してのみ、使用可能です – 浮動小数点数と構造体には使用不可 ● スピードは、一時変数を用いるものより遅い です 解決可能! 75
  • 76. SWAPマクロ xor SWAP:安全版(1) ● 同一の変数かどうかをチェックするために、三項演 算子を組み込みます #define SWAP(a, b) (((a) == (b)) ? 0 : ( *(a) ^= *(b), *(b) ^= *(a), *(a) ^= *(b), 1 )) ● 条件判断が加わる分、処理が増えますが・・・ – aとbのアドレスがコンパイル段階で異なると分かるとき は、最適化により条件判断部分は消えます 76
  • 77. SWAPマクロ xor SWAP:安全版(2) ● 短絡評価を用いると以下のように、カッコよ く書けます! #define SWAP(a, b) (((a) != (b)) && (*(a) ^= *(b) ^= *(a) ^= *(b), 1)) カッコイイ! 77
  • 78. SWAPマクロ 加減算SWAP ● 足し算と引き算を用いたSWAPアルゴリズムです #define SWAP(a, b) ( *(a) += *(b), *(b) = *(a) - *(b), *(a) -= *(b) ) ● 変数宣言の必要がないため、カンマ演算子で連結するこ とにより、置換後全体を式化しています ● よりカッコよく書くと↓ – ※コンパイラの警告レベルを上げると、警告が出ます #define SWAP(a, b) (*(a) += *(b) -= *(a) = *(b) - *(a)) 78
  • 79. SWAPマクロ 加減算SWAP:問題点 ● 同一の変数に対して用いた場合、変数の値が0 になります ● 整数と浮動小数点の変数に対してのみ、使用 可能です – 構造体には使用不可 ● 浮動小数点数に用いた場合、丸め誤差が発生 する場合があります ● スピードは、一時変数を用いるものやxorを用 いるものよりも遅いです 79
  • 80. SWAPマクロ 加減算SWAP:問題点 ● 同一の変数に対して用いた場合、変数の値が0 になります ● 整数と浮動小数点の変数に対してのみ、使用 可能です – 構造体には使用不可 解決可能! ● 浮動小数点数に用いた場合、丸め誤差が発生 する場合があります ● スピードは、一時変数を用いるものやxorを用 いるものよりも遅いです 80
  • 81. SWAPマクロ 加減算SWAP:安全版(1) ● 同一の変数かどうかをチェックするために、三項演 算子を組み込みます #define SWAP(a, b) (((a) == (b)) ? 0 : ( *(a) += *(b), *(b) = *(a) - *(b), *(a) -= *(b), 1 )) ● 条件判断が加わる分、処理が増えますが・・・ – aとbのアドレスがコンパイル段階で異なると分かるとき は、最適化により条件判断部分は消えます 81
  • 82. SWAPマクロ 加減算SWAP:安全版(2) ● 短絡評価を用いると以下のように、カッコよ く書けます! #define SWAP(a, b) (((a) != (b)) && (*(a) += *(b) -= *(a) = *(b) - *(a), 1)) カッコイイ!! 82
  • 83. SWAPマクロ GNU拡張文法SWAP ● gccのC言語拡張文法を使用したマクロです – typeof演算子 – 複文の式化 ● シームレスかつ性能もいいです ● 最強のSWAPマクロ? #define SWAP(a, b) ({ typeof(*(a)) __tmp_swap_var__ = *(a); *(a) = *(b); *(b) = __tmp_swap_var__; }) 83
  • 84. アウトライン(2) ● マクロテクニック – 文字列化演算子 – トークン連結演算子 – 可変引数マクロ ● 黒魔術 – printlnマクロ – until文、unless文 – デバッグ用マクロ – SWAPマクロ – ダフのデバイス 84
  • 85. ダフのデバイス 概要 ● Tom Duffという人が考案しました ● ループのアンロールに用いられます – ループの条件判断回数を減らすことにより、高速 化を目指しています ● switch文のfall-throughを利用しています ● caseラベルをまたがるdo ~ while文が特徴です – C言語のswitch文は、規定が弱いから利用可能 – JavaやC#のswitch文では、利用不可 85
  • 86. ダフのデバイス コード(1) ● ダフのデバイスによる、配列のコピーを行う 関数です void duff_memcpy(int *to, const int *from, int size) { int n = (size + 7) >> 3; switch (size & 7) { case 0: do { *to++ = *from++; case 7: *to++ = *from++; case 6: *to++ = *from++; case 5: *to++ = *from++; case 4: *to++ = *from++; case 3: *to++ = *from++; case 2: *to++ = *from++; case 1: *to++ = *from++; } while (--n); } } 86
  • 87. ダフのデバイス コード(2) ● 次のように書くこともできます void duff_memcpy(int *to, const int *from, int size) { switch (size & 7) { case 0: do { *to++ = *from++; case 7: *to++ = *from++; case 6: *to++ = *from++; case 5: *to++ = *from++; case 4: *to++ = *from++; case 3: *to++ = *from++; case 2: *to++ = *from++; case 1: *to++ = *from++; } while (size -= 8); } } 87
  • 88. ダフのデバイス 特徴 ● 今回取り上げた例では、8回毎のアンロール処 理でしたが、もっと回数を増やすことも可能 です ● 現代では、あまり大きく性能向上に寄与しま せん(アンロール回数を256回毎程度にする と、良くなりますが・・・) ● コード量(つまり、実行バイナリのサイズ)が 少し増えます 88
  • 89. ダフのデバイス 要望 ● メモリコピー以外に応用したい! ● 同じ処理を記述してくのはめんどくさい! 89
  • 90. ダフのデバイス 要望 ● メモリコピー以外に応用したい! ● 同じ処理を記述してくのはめんどくさい! ● ええ、解決できますとも! ● そう、関数マクロならね 90
  • 91. ダフのデバイス 関数マクロへの応用 ● 以下の<処理>の部分に、行いたい処理が来る とよいですよね void duff_memcpy(int *to, const int *from, int size) { int n = (size + 7) >> 3; switch (size & 7) { case 0: do { <処理>; case 7: <処理>; case 6: <処理>; case 5: <処理>; case 4: <処理>; case 3: <処理>; case 2: <処理>; case 1: <処理>; } while (--n); } } 91
  • 92. ダフのデバイス 関数マクロへの応用 ● ダフのデバイスをマクロ化すると、以下のよ うになります – STATEMENT:行いたい処理(式または複文) – __VA_ARGS__:次のステップ前の処理(省略化) #define DUFFS_LOOP(n, STATEMENT, ...) { register int __tmp_loop_var__ = ((n) + 7) >> 3; switch ((n) & 7) { case 0: do { STATEMENT; __VA_ARGS__; case 7: STATEMENT; __VA_ARGS__; case 6: STATEMENT; __VA_ARGS__; case 5: STATEMENT; __VA_ARGS__; case 4: STATEMENT; __VA_ARGS__; case 3: STATEMENT; __VA_ARGS__; case 2: STATEMENT; __VA_ARGS__; case 1: STATEMENT; __VA_ARGS__; } while (--__tmp_loop_var__); } 92 }
  • 93. ダフのデバイス 関数マクロへの応用 ● 使用例: int a = 0; DUFFS_LOOP(10, { printf(“a = %dn”, a); }, a++); ● 複文が引数となるので、呼び出し側からは、 奇妙な形に見えますね 93
  • 94. 参考文献 ● プログラミング言語C 第2版 94
  • 95. 参考文献 ● トリッキーコードネット – http://tricky-code.net/ ● 苦しんで覚えるC言語 – http://9cguide.appspot.com/ ● C言語の小技:SWAPマクロ – http://no1-bug-creator.cocolog-nifty.com/blog/2011/08/cswap-2ccd.html ● Duff's device – http://ja.wikipedia.org/wiki/Duff's_device ● ダフのデバイスというのを初めて知った - 星一の日記 – http://d.hatena.ne.jp/hajimehoshi/20080428/1209314296 95
  • 98. 実際のコーディングの 役に立たない 知識ばかり でしたね? 98
  • 99. でも、 99
  • 100. ベテランの書いた C言語ソースを読むときに 役に立つかも しれません 100
  • 102. そして何より、 関数マクロを 書く助けになったのなら 嬉しいです! 102