CMakeを見る





新しい仕事に就いたとき、私はこの会社で使用されている新しいテクノロジーを素早く習得しなければなりませんでした。 そのような技術の1つはcmakeビルドシステムであり、これは私がこれまでに遭遇したことのないものです。



このシステムには、アセンブリスクリプトを記述するための独自の組み込み言語があります。 この言語に興味がありました。 すぐに、数式を計算したり、ファイルから読み書きしたり、外部プロセスやその他の興味深い機能を開始したりできることがわかったので、この言語をメイン言語として使用し、具体的なものを書くことを考えました。 cmake 2.8でどのように時計を書いたかについてです。



正直なところ、最初に標準ストリームからの入出力の可能性についてcmakeをチェックするというアイデアを思いつきました。 押されたキー、または最悪の場合はマウスイベントを読み取る方法を学びたかったので、テトリスなどのインタラクティブなプログラムを作成できるようになりました。 結論は非常に簡単であることが判明しました。
 ファイル(WRITE / dev / stdout "blabla") 
しかし、cmakeは標準ストリームの読み取りを完全に拒否し、イベント(/ dev / input / event4または/ dev / input / mice)から直接読み取ることもできませんでした。 したがって、テトリスを作成するというアイデアは拒否され、出力をいじることに決めましたが、標準出力に直接出力する機能は標準のメッセージ()コマンドよりも魅力的でした。



私は、stdoutに直接書き込むことができるので、 そこにエスケープシーケンスを記述しようとすることにしました 。 これにより、色の出力、カーソルの移動、画面のクリーニングなどの豊富な機会が得られます。 幸いなことに、cmakeは印刷できない文字を出力できることが判明しました。これは文字列関数のASCII操作であるため、スクリーンクリーニング関数を作成しました。

文字列(ASCII 27 ESCAPE)
関数(clrscr)
    ファイル(WRITE / dev / stdout "$ {ESCAPE} [2J")
終了関数(clrscr)


エスケープコードが機能したため、テキストを任意の座標で表示するための次のステップを学ぶことにしました。

関数(textXY XY MSG)
    ファイル(WRITE / dev / stdout "$ {ESCAPE} [$ {Y}; $ {X} H $ {MSG}")
終了関数(textXY)


さて、これの次の論理的継続は、線描画関数を書くというアイデアでした。 ここではすでに最初の困難に直面しなければなりませんでした:

  1. cmakeは、文字列に書き込まれた式を評価し、その結果は整数です。
  2. cmakeの関数は値を返しません。
  3. 互いの端の位置に依存しない汎用の線描画アルゴリズムが必要です。


私はこれらの困難を最後から解決し始めました。 最初に、線描画アルゴリズムが発明されました。

  1. マイナスを考慮して、端の座標の差Dx = x2-x1とDy = y2-y1を見つけます(方向に必要です)。
  2. 最大モジュラスデルタを見つけるDmax = max(abs(Dx)、abs(Dy));
  3. サイクルi = 0..Dmaxを実行し、各ステップで式を使用して現在の座標を計算します。
    	 x = x1 + i * Dx / Dmax
    	 y = y1 + i * Dy / Dmax 


第二に、最大値と絶対値を見つける機能が必要でした。 cmakeの関数は値を返さないため、マクロを使用する必要がありました。 マクロでは、変数と値の両方を置き換えることができます。 どこでも変数を置換するほうがきれいに見えますが、マクロは「毛むくじゃら」すぎるため、将来は結果に対してのみ変数置換を使用し始めました。



マクロコード
マクロ(最大abm)
	 if($ {a} LESS $ {$ {b}})
		セット($ {m} $ {$ {b}})
	その他($ {a}少ない$ {$ {b}})
		セット($ {m} $ {$ {a}})
	 endif($ {a} LESS $ {$ {b}})
エンドマクロ(最大)

マクロ(abs a res)
	 if($ {a} LESS 0)
		文字列(LENGTH $ {a} len)
		数学(EXPR l1 "$ {len}-1")
		文字列(SUBSTRING $ {a} 1 $ {l1} $ {res})
	その他($ {a}少ない0)
		セット($ {res} $ {a})
	 endif($ {a}少ない0)
エンドマクロ(abs)




絶対値を見つけるには、cmakeが文字列で動作し、マイナスがある場合は単純に「食い止める」という事実が使用されます。



マクロの準備ができたとき、コマンドを使用して座標の式を計算しようとしたとき
 数学(EXPR <結果> <式>) 
cmakeが文字列で動作するという事実に関連する興味深いニュアンスを理解しました。したがって、たとえば、式「$ {a} + $ {b}」は、bが負の場合、計算されません(結果として、 5 + -6のようなものですが、そのような式は無効です)。 微妙なルールでこのニュアンスを回避することができました-変数の負の値が式で見つかる場合はいつでも、先頭に0を追加し、すべてを括弧で囲みます: "$ {a} +(0 $ {b})"。 結果の線描画関数は次のとおりです。



機能コード行(x1 y1 x2 y2 chr)
関数(行x1 y1 x2 y2 chr)
	数学(EXPR Dx "$ {x2}-$ {x1}")
	 abs($ {Dx} aDx)
	数学(EXPR Dy "$ {y2}-$ {y1}")
	 abs($ {Dy} aDy)
	 max(aDx aDy Dmax)
	セット(i 0)
	 while(i LESS $ {Dmax})
		数学(EXPR cx "$ {x1} + $ {i} *(0 $ {Dx})/ $ {Dmax}")
		数学(EXPR cy "$ {y1} + $ {i} *(0 $ {Dy})/ $ {Dmax}")
		 textXY($ {cx} $ {cy} $ {chr})
		数学(EXPR i "$ {i} + 1")
	終了(i LESS $ {Dmax})
 endfunction(行)




線の描画機能をテストした後、アイデアはどこかでそれを適用するようになりました(たとえば、クロックを「ガッシュ」する)。 それ以前は、これで何か面白いことができるとはまったく知りませんでした。 ほとんどすべての準備ができていることがわかりました。ダイヤルを描画し、システムから時間を取得し、必要な角度を計算し、正しい角度(時間、分、秒針)で3本の線を描画すると、時計の準備が整います。 まだ2つの関数がありません。正弦と余弦、円を描くため、そして与えられた角度で線を描くためです。



サインとコサインの値が範囲[0; 1]であり、cmakeが整数値でのみ動作するため、問題は複雑でした。したがって、係数1000を使用することに決定しました。この比率で。



三角関数を実装するために、 Maclaurinシリーズでの展開が使用されます。 そして再び困難:

  1. Maclaurinシリーズであまりにも高い度合いと階乗を使いたくありません。
  2. シリーズの最初の2〜3項を使用する場合、良好な近似は区間[-pi / 2;でのみ得られます。 pi / 2]。


少なくとも間隔[-pi;でODZが必要でした。 2 * pi]。このため、ラジアン単位の角度を右半平面に変換し、関数の符号を修正することにしました。 技術的には、 幾何学的な意味と簡約式がありますので、あまり噛みません。 結果の三角関数のコードはかなりcodeいです:



サインおよびコサインコード
セット(PI1000 3142)
セット(PI500 1571)
セット(_PI500 -1571)
設定(_2PI1000 6283)

マクロ(m_rad1000_4sin x res)
	数学(EXPR rad1000 "(0 $ {x})* $ {PI1000} / 180")
	 if(rad1000 GREATER $ {PI1000})
		数学(EXPR rad1000_ "$ {PI1000}-$ {rad1000}")
	その他(rad1000 GREATER $ {PI1000})
		セット(rad1000_ $ {rad1000})
	 endif(rad1000 GREATER $ {PI1000})
	
	 if(rad1000_ GREATER $ {PI500})
		数学(EXPR rad1000__ "$ {PI1000}-$ {rad1000_}")
	その他(rad1000_ GREATER $ {PI500})
		 if(rad1000_ LESS $ {_ PI500})
			 abs($ {rad1000_} abs_rad1000_)
			数学(EXPR rad1000__ "$ {abs_rad1000_}-$ {PI1000}")
		その他(rad1000_ LESS $ {_ PI500})
			セット(rad1000__ $ {rad1000_})
		 endif(rad1000_ LESS $ {_ PI500})
	 endif(rad1000_ GREATER $ {PI500})
	
	セット($ {res} $ {rad1000__})
エンドマクロ(m_rad1000_4sin)

マクロ(m_rad1000_4cos x res)
	数学(EXPR rad1000 "(0 $ {x})* $ {PI1000} / 180")
	 if(rad1000 GREATER $ {PI1000})
		数学(EXPR rad1000_ "$ {rad1000}-$ {_ 2PI1000}")
	その他(rad1000 GREATER $ {PI1000})
		セット(rad1000_ $ {rad1000})
	 endif(rad1000 GREATER $ {PI1000})
	
	セット($ {res} $ {rad1000_})
エンドマクロ(m_rad1000_4cos)

マクロ(sin1000 x res)
	 m_rad1000_4sin($ {x} r1000)
	数学(EXPR $ {res} "0 $ {r1000}-(0 $ {r1000})*(0 $ {r1000})/ 1000 *(0 $ {r1000})/ 1000/6 +(0 $ {r1000} )*(0 $ {r1000})/ 1000 *(0 $ {r1000})/ 1000 *(0 $ {r1000})/ 1000 *(0 $ {r1000})/ 1000/120 ")
エンドマクロ(sin1000)

マクロ(cos1000 x res)
	 m_rad1000_4cos($ {x} r1000)
	設定解除(サイン)
	 if(r1000 GREATER $ {PI500})
		数学(EXPR r1000_ "$ {PI1000}-$ {r1000}")
		セット(r1000 $ {r1000_})
		セット(符号「0-」)
	 endif(r1000 GREATER $ {PI500})
	
	 if(r1000 LESS $ {_ PI500})
		数学(EXPR r1000_ "$ {PI1000} +(0 $ {r1000})"
		セット(r1000 $ {r1000_})
		セット(符号「0-」)
	 endif(r1000 LESS $ {_ PI500})
	
	数学(EXPR $ {res} "$ {sign}(1000-(0 $ {r1000})*(0 $ {r1000})/ 1000/2 +(0 $ {r1000})*(0 $ {r1000}) / 1000 *(0 $ {r1000})/ 1000 *(0 $ {r1000})/ 1000/24-(0 $ {r1000})*(0 $ {r1000})/ 1000 *(0 $ {r1000}) / 1000 *(0 $ {r1000})/ 1000 *(0 $ {r1000})/ 1000 *(0 $ {r1000})/ 1000/720) ")
エンドマクロ(cos1000)




その後、残りはすでに技術的な問題でした-円で12個の数字を描き、サイクルを回して、システムに時間を求めます。 変更されたら、古い矢印を消去し、新しい矢印を直角に描きます。 外部プロセスの起動を通じて時間を取得します。
  execute_process(COMMAND "date" "+%H%M%S" OUTPUT_VARIABLE時間) 
時間から部分文字列を選択し、角度を計算します-学校数学の枠組みで。



完全なコードはgithubで表示できます。

cmakeバージョン2.8.12.2、Ubuntu 12.04、14.04でテスト済み。



All Articles