web-dev-qa-db-ja.com

最新のプログラミング言語のほとんどにマクロが含まれていないのはなぜですか?

それらがC/C++で非常に危険に実装されていることを知っています。より安全な方法で実装することはできませんか?マクロの欠点は、マクロが提供する大規模なパワーを上回るほど本当に悪いのでしょうか?

33
Casebash

主な理由は、マクロがlexicalであることです。これにはいくつかの影響があります。

  • コンパイラーは、マクロが意味的に閉じていること、つまり、マクロが関数のように「意味の単位」を表していることを確認する方法がありません。 (_#define TWO 1+1_を検討してください— _TWO*TWO_は何に等しいですか?3.)

  • マクロは関数のように型付けされません。コンパイラーは、パラメーターと戻り値の型が適切であることを確認できません。マクロを使用する拡張式のみをチェックできます。

  • コードがコンパイルされない場合、コンパイラーはエラーがマクロ自体にあるのか、マクロが使用される場所にあるのかを知る方法がありません。コンパイラは、半分の時間で間違った場所を報告するか、片方がおそらく問題がないとしても、両方を報告する必要があります。 (#define min(x,y) (((x)<(y))?(x):(y))を検討してください:xyの型が一致しない、または_operator<_を実装していない場合、コンパイラは何をすべきですか?)

  • 自動化されたツールは、意味的に有用な方法でそれらを使用することはできません。特に、関数のように機能するが式に拡張するマクロにはIntelliSenseのようなものはありません。 (ここでも、minの例です。)

  • マクロの副作用は、関数の場合ほど明確ではないため、プログラマーに混乱を招く可能性があります。 (もう一度minの例を考えてみます。関数呼び出しでは、xの式が1回しか評価されないことがわかりますが、ここではマクロを調べないとわかりません。)

私が言ったように、これらはすべてマクロが語彙であるという事実の結果です。それらをより適切なものに変えようとすると、最終的には関数と定数になります。

58
Timwi

しかし、はい、マクロcanは、C/C++よりも優れた設計と実装が可能です。

マクロの問題は、マクロがコードを別の何かに書き換える言語構文拡張メカニズムであることです。

  • C/C++の場合、基本的な健全性チェックはありません。気をつければ大丈夫。間違いを犯したり、マクロを使いすぎたりすると、大きな問題が発生する可能性があります。

    これに加えて、(C/C++スタイルの)マクロで実行できる多くの簡単なことを、他の言語では他の方法で行うことができます。

  • さまざまなLISP方言などの他の言語では、マクロはコア言語構文とよりよく統合されていますが、マクロ「リーク」の宣言で問題が発生する可能性があります。これは 衛生的なマクロ によって対処されます。


簡単な歴史的背景

マクロ(マクロ命令の略)は、アセンブリ言語のコンテキストで最初に登場しました。 Wikipedia によると、1950年代には一部のIBMアセンブラでマクロが利用可能でした。

元のLISPにはマクロがありませんでしたが、1960年代中頃にMacLispに最初に導入されました: https://stackoverflow.com/questions/3065606/when-did-the-idea-of-macros-user -defined-code-transformation-appearhttp://www.csee.umbc.edu/courses/331/resources/papers/Evolution-of-LISP.pdf 。それ以前は、「fexprs」がマクロのような機能を提供していました。

Cの最も古いバージョンにはマクロがありませんでした( http://cm.bell-labs.com/cm/cs/who/dmr/chist.html )。これらは、プリプロセッサを介して1972〜73年頃に追加されました。それ以前は、Cは#includeおよび#defineのみをサポートしていました。

M4マクロプリプロセッサは1977年頃に生まれました。

最近の言語では、操作のモデルがテキストではなく構文であるマクロが実装されているようです。

したがって、誰かが「マクロ」という用語の特定の定義のprimacyについて話すとき、意味が時間とともに進化していることに注意することが重要です。

13
Stephen C

マクロは Scottが注記しているように で、ロジックを隠すことができます。もちろん、関数、クラス、ライブラリ、その他多くの一般的なデバイスも同様です。

しかし、強力なマクロシステムはさらに進んで、言語には通常見られない構文や構造を設計して利用することができます。これは確かに素晴らしいツールです。ドメイン固有の言語、コードジェネレーターなど、すべて単一の言語環境で快適に...

ただし、悪用される可能性があります。これにより、コードの読み取り、理解、デバッグが困難になり、新しいプログラマーがコードベースに慣れるために必要な時間が増加し、コストのかかるミスや遅延につながる可能性があります。

したがって、プログラミングを簡略化することを目的とした言語(JavaまたはPython)など)の場合、このようなシステムは嫌悪感を覚えます。

12
Shog9

マクロは、状況によっては非常に安全に実装できます。たとえば、LISPでは、マクロは変換されたコードをデータ構造(s-式)として返す関数にすぎません。もちろん、LISPは homoiconic であるという事実と「コードはデータである」という事実から大きな利益を得ます。

マクロがいかに簡単であるかの例は、例外の場合に使用されるデフォルト値を指定するこのClojureの例です。

(defmacro on-error [default-value code]
  `(try ~code (catch Exception ~'e ~default-value)))

(on-error 0 (+ nil nil))               ;; would normally throw NullPointerException
=> 0                                   ;l; but we get the default value

Lispでさえ、一般的なアドバイスは「必要でない限りマクロを使用しないこと」です。

同型の言語を使用していない場合、マクロはよりトリッキーになり、他のさまざまなオプションにはすべていくつかの落とし穴があります。

  • テキストベースのマクロ-例Cプリプロセッサ-実装は簡単ですが、構文の癖を含むテキスト形式で正しいソース構文を生成する必要があるため、正しく使用するには非常にトリッキーです
  • マクロベースのDSLS-例C++テンプレートシステム。複雑で、それ自体がいくつかのトリッキーな構文になる可能性があり、コンパイラーとツールの作成者が正しく処理するために非常に複雑になる可能性があります。
  • AST /バイトコード操作API-例Javaリフレクション/バイトコード生成-理論的には非常に柔軟ですが、非常に冗長になる可能性があります。非常に単純なことを行うには多くのコードが必要になる場合があります。同等のコードを生成するために10行のコードが必要な場合3行の関数、そしてあなたはあなたのメタプログラミング努力であまり得ていません...

さらに、マクロが実行できるすべてのことは、最終的には完全な別の言語で他の方法で実現できます(これが定型文をたくさん書くことを意味する場合でも)。このすべての巧妙さの結果として、多くの言語がマクロを実装するためのすべての努力に本当に価値がないと多くの言語が決定することは驚くことではありません。

6
mikera

質問に答えるために、主にどのマクロが使用されているかを考えてください(警告:脳でコンパイルされたコード)。

  • シンボリック定数の定義に使用されるマクロ_#define X 100_

これは簡単に置き換えることができます:_const int X = 100;_

  • (本質的に)インライン型に依存しない関数を定義するために使用されるマクロ#define max(X,Y) (X>Y?X:Y)

関数のオーバーロードをサポートする任意の言語では、正しい型のオーバーロードされた関数を使用することにより、またはジェネリックをサポートする言語ではジェネリック関数によって、タイプセーフな方法でこれをエミュレートできます。マクロは喜んで比較を試みますanythingを含むポインタまたは文字列を含みますが、コンパイルされる可能性がありますが、たいていは望んだものではありません。一方、マクロをタイプセーフにした場合、オーバーロードされた関数に比べて利点や利便性はありません。

  • 頻繁に使用される要素へのショートカットを指定するために使用されるマクロ。 _#define p printf_

これは、同じことを行う関数p()で簡単に置き換えることができます。これはC(関数のva_arg()ファミリを使用する必要がある)にかなり関与していますが、関数の引数の可変数をサポートする他の多くの言語では、はるかに簡単です。

これらの機能のサポートwithin特別なマクロ言語ではなく言語のほうが単純で、エラーが発生しにくく、コードを読む他の人にとって混乱がはるかに少なくなります。実際、別の方法で簡単に複製できないマクロのsingleユースケースを考えることはできません。マクロが本当に役立つonlyの場所は、マクロが_#if_(など)のような条件付きコンパイル構造に関連付けられている場合です。

一般的な言語での条件付きコンパイルに対する非プリプロセッサソリューションは非常に扱いにくい(Javaでのバイトコードインジェクションのように)と思うので、その点については、私はあなたと議論しません。しかし、Dなどの言語は、プリプロセッサを必要とせず、プリプロセッサ条件文を使用するよりも面倒なソリューションを考え出しましたが、エラーが発生しにくくなっています。

5
Chinmay Kanchi

C/C++のMACROは非常に制限されており、エラーが発生しやすく、それほど有用ではないことに注意してください。

LISP、z/OSアセンブラー言語などで実装されているMACROは、信頼性が高く、非常に便利です。

しかし、Cの制限された機能の乱用のため、彼らは悪い評判を集めています。そのため、誰もマクロを実装しなくなりました。代わりに、実行するために使用される単純なスタッフマクロの一部を実行するテンプレートや、実行するために使用されるより複雑なスタッフマクロの一部を実行するJavaの注釈などを取得します。

2
James Anderson

マクロに関して私が見た最大の問題は、頻繁に使用すると、簡単に見つけられない(または簡単ではない)マクロ内のロジックを非表示にすることができるため、コードの読み取りと保守が非常に困難になることです。 )。

2
Scott Dorman