質問

私はDのと思います static if 興味深い言語機能です。それが私の質問を促します:コンパイラがコードの強力な概念を持っている文化言語の他の例はありますか、そしてそれらにアクセスするための言語施設がありますか?

たとえば、このコードはに似たものを提供します repr Pythonから:

char[] repr(T)(T value) {
  static if (is(typeof(value.__repr__))) { // class T provides a "repr()" method
    return value.__repr__();  
  } else static if (is(T:string)) {
    return `"` ~ value ~ `"`;
  // ...other cases...
  } else {
    return toString(value);
  }
}

これはクールだと思います。これは、過負荷が行うことに対する異なる一般的なアプローチを可能にするからです。たとえば、コンパイラは私のクラスのフィールドの数を知っていますが、私のコードがほとんどの言語でコンパイル時にその情報にアクセスする方法はありません。

警告:最後の段落には意見がありましたが、論争を引き出すのではなく、私の質問に何らかの動機と説明を提供することを意味します。他のコンパイルされた言語にそのような機能があるかどうかを知りたいだけです。

役に立ちましたか?

解決

本物のマクロを持つ言語には、静的の形式があります。たとえば、lisp and ネマル マクロが拡張するコードを構築して、「if」やfor-loopsなどのプログラミングコンストラクトを使用します。これらは本質的にタイムの決定をコンパイルし、static ifに似たことを行うことができます。 Nemerleの場合、マクロは基本的にコンパイル時に実行されるコンパイラへのプラグインです。

C ++にはあります ブーストMPL あるライブラリ 一種の静的if これは、2つのタイプから選択するために使用できます。 run()メンバーに2つのタイプの内側にいくつかのコードを配置して、ちょっと似たようなものを取得できますが、非常に面倒な構文を使用できます。

たとえば、Boost MPLを使用すると、次のようなことができます。

struct float_impl { 
    static void run() { /* float case code */ }
}
struct int_impl { 
    static void run() { /* int case code */ }
}

typedef typename if_<
          is_same<T, float>
        , float_impl
        , int_impl
        >::type impl_t;
impl_t::run();

Dでそれは次のとおりです。

static if(is(T == float)) {
     /* float code */
}
else {
     /* int code */
}

他のヒント

「言語のコードの認識」については、LISPとそのマクロ施設、特に一般的なLISPよりも良いことはありません。しかし、そこでの取引は、ほとんどの場合、オブジェクトのタイプがコンパイル時間またはマクロエクスポンション時間で知られていないことです。リテラルの場合、タイプは既知であるため、オブジェクトがリテラルであるかどうかを確認するためにテストする攻撃的なマクロの例を見つけることができ、もしそうなら、それを一つの方法で扱います - おそらくそのタイプに基づいて - それ以外の場合は検出された変数を準備しますランタイムタイプ検査用。

これが私がから適応した例です cllib ライブラリ(の一部 CLOCC 図書館)数年前。目標は、一致するプレフィックスを使用して他の文字列のプレフィックス文字列を切り刻む関数を提供することです。接頭辞は、マクロ拡張時間で知られている場合があります。そうでない場合があります。もしそうなら、最適化:最初に接頭辞の長さを計算し、それを文字通りとして埋め込んで、生成された関数への各呼び出しで再計算されないようにします。マクロは最初は困難ですが、実際の生成コードは小さいです。

(defmacro after-prefix-core (comparison-op prefix string &optional length)
  "Similar to cllib:string-beg-with-cs."
  (flet ((chop (prefix prefix-length string string-length)
           `(when (and (>= ,string-length ,prefix-length)
                       (,comparison-op ,prefix ,string :end2 ,prefix-length))
              (subseq ,string ,prefix-length ,string-length))))
    (let* ((gstring (gensym "STRING-"))
           (gstring-length (gensym "STRING-LENGTH-")))
      `(let* ((,gstring ,string)
              (,gstring-length ,(or length `(length ,gstring))))
         ,(if (stringp prefix)
              ;; Constant -- length known at expansion time.
              (let ((prefix-length (length prefix)))
                (chop prefix prefix-length gstring gstring-length))
              ;; Other form -- length not known at expansion time.
              (let ((gprefix (gensym "PREFIX-"))
                    (gprefix-length (gensym "PREFIX-LENGTH-")))
                `(let* ((,gprefix ,prefix)
                        (,gprefix-length (length ,gprefix)))
                   ,(chop gprefix gprefix-length gstring gstring-length))))))))


(defmacro after-prefix (prefix string &optional length)
  "Similar to cllib:string-beg-with."
  `(after-prefix-core string-equal ,prefix ,string ,length))


(defmacro after-prefix-cs (prefix string &optional length)
  "Similar to cllib:string-beg-with-cs."
  `(after-prefix-core string= ,prefix ,string ,length))

フォームを参照してください

(if (stringp prefix)

途中で?それは、マクロ拡張時間での最初の議論を検査しており、引数が文字通りであるかシンボルであるかに応じて、そのタイプが知られている場合と知られていない場合があります。タイプがシンボルの場合、私たち 推定 実行時間まで、他の価値を指す変数として再検討するまで待つ必要があります。

これがフォームの拡張です (after-prefix foo bar):

(LET* ((#:STRING-5340 BAR) (#:STRING-LENGTH-5341 (LENGTH #:STRING-5340)))
  (LET* ((#:PREFIX-5342 FOO) (#:PREFIX-LENGTH-5343 (LENGTH #:PREFIX-5342)))
    (WHEN
        (AND (>= #:STRING-LENGTH-5341 #:PREFIX-LENGTH-5343)
             (STRING-EQUAL #:PREFIX-5342 #:STRING-5340 :END2 #:PREFIX-LENGTH-5343))
      (SUBSEQ #:STRING-5340 #:PREFIX-LENGTH-5343 #:STRING-LENGTH-5341))))

変数に注意してください #:PREFIX-LENGTH-5343 にバインドされています 計算された長さFOO, 、ここで可変に結び付けます #:PREFIX-5342.

次に、フォームの拡張を見てください (after-prefix "foo" bar), 、プレフィックスが文字列リテラルになりました:

(LET* ((#:STRING-5463 BAR) (#:STRING-LENGTH-5464 (LENGTH #:STRING-5463)))
  (WHEN (AND (>= #:STRING-LENGTH-5464 3) (STRING-EQUAL "foo" #:STRING-5463 :END2 3))
    (SUBSEQ #:STRING-5463 3 #:STRING-LENGTH-5464)))

現在、「foo」の長さを計算することはありません。 3としてインラードされています。

この例ではあまりにも多くの作業のように思えるかもしれませんが、そのようなことをすることができることは、あなたの質問が意見を述べるように、持つべき良い力です。

static_if 次のバージョンのC ++(C ++ 1Y)に提案されています。もともとはC ++ 11のために提案されていましたが、明らかに遅れていました。

提案を参照してください ここ. 。興味深いことに、著者の一人はDの作成者であるWalter Brightです。

また、 コンパイラハックを使用して現在のC ++でstatic-ifを偽造することができます.

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top