>>56
WPFについては、解説を書けるほどの知識を現時点で持っておらず、また今後使うことがあるとしても.NET MAUIになると思っています。
ですのでリクエスト頂いたところ恐縮ですが、WPFについてはご期待にお応えすることができません。
.NET 同様の解説法でWPFについてもお願いします。
>>54
ご指摘の通りキューの動作は「先入れ先出し」となります。
記載内容に誤りがあったので修正しました。
ご指摘いただきありがとうございました。
https://smdn.jp/programming/netfx/collections/2_generic_5_2_queue/
キューは先入先出ではないのでしょうか。
>>52
ご質問ありがとうございます。
制限事項としても明記していますが、例示いただいた式のような
「暗黙の乗算を含む部分式に関する動作は未定義」となります。
ですので、ご指摘のとおり本来なら計算成功ではありませんが、
現状の動作で仕様通りということになります。
現時点での実装では、こういった式をエラーとするような処理は
実装していないため、エラー報告はされずに処理が継続し、
最終的には計算成功扱いとなるという動作になっています。
また、式"2(1+2)"は"2*(1+2)"のように暗黙の乗算を含む式とは解釈されず、
計算できない単一の項"2(1+2)"として扱われそのまま出力されるため、
計算成功扱いにはなりますが期待するような結果は出力されないという動作になります。
はじめまして。
プログラムの参考に閲覧しました。
プログラムに疑問がわいたのでこちらに失礼します。
Programming / Tips / 二分木を使った数式の逆ポーランド記法化と計算
にある最後のC言語でのコードについて。
入力計算式を
1 (10 * 10) / 10
などとカッコの前後に演算子のない数値を記入したとき。
計算成功,になりますが,計算結果はでたらめです。
本来は計算成功ではないと思われますが,これは仕様なのでしょうか。
初歩的な質問で,失礼しました。
>>50
以下、変更箇所の差分です。
>>48
大変遅くなりましたが、頂いたご指摘をもとに修正した内容を先ほど公開いたしました。
変更した箇所としなかった箇所は下記のとおりとなります。
パフォーマンス関連での修正にはご満足いただけない部分があるかとは思いますが、ご確認いただければと思います。
変更した箇所
以下のようにすればi=0からスキャンになり冒頭の見かけ上の冗長さはなくなるが、逆に開き括弧が出現するたびにif (0 == i)が評価されることになり、最終的な判断としては変更なしに
変更しなかった箇所
cond ? cond-true : cond-false
, func(arg1, arg2, arg3...)
)を考えると、二分木の場合に特化した最適化の感もある変更箇所の差分を次の投稿にて別途掲載しますので合わせてご覧ください。 また、上記以外(コード以外)の変更点については下記の更新情報をご参照ください。
http://smdn.jp/programming/tips/polish/#DocumentLog
改めて、ご指摘をいただきありがとうございました。
詳細にわたりレビューしていただきありがとうございます。
C言語は普段使いの言語ではないため細部が甘くなりがちで、またこういった意見を
いただく機会もなかなかないので、大変ありがたく思います。
指摘いただいた箇所については反映したい部分が多々ありますが、修正が完了するまでには
また時間をいただくことになりそうなので、勝手ながら留意事項として本文中に
書き込みへのリンクを貼らせていただく形での暫定的な対応とさせていただきました。
(http://smdn.jp/programming/tips/polish/#Implementation_C)
現状本文の方も変更を考えている部分があるので、それと合わせて修正後に
また改めてお知らせいたします。
(おそらく来年2018年1月中旬〜下旬ごろになると思います)
なお、以下は頂いた部分についての現時点での修正対応予定です。
概ね、コードの簡略化・読みやすさを優先とし、恐縮ながら速度の向上が得られる
のみとなる修正はしない、という方針に基づく判断となります。
実用ではなくあくまで理解のためのサンプルコードという位置づけのため、また扱う文字列長も
高々キロバイト単位のため、実用や高速化を目的とした改善はコードの利用者各自で
行ってもらいたく思っています。
速度を意識した改善は行いたくはありますが、あくまでサンプルコードとしての位置づけでの公開ですし、
また実用を考慮しだすと速度以外にも変更したい部分は多々あります。
例えば、演算子も*や/ではなく×や÷を使えるようにしたい、その他記号等も使用したいと考えると
char配列ではなく非ASCIIに対応する文字列型を使うべき。 あるいは計算では浮動小数点ではなく
固定小数点を使うべき、など。 これらに対応するとコード量が増えてサンプルとしては長大になるため、
ここで扱うべき範囲を超えるとして省略しています。
もちろん、memsetやstrlenに関する点は実用を主眼とした場合には有用ですので、今後コードを書く際の
参考にしたいと思います。
いずれにせよ、改善に繋がるご意見は大変ありがたいので、他にももし改良点がありましたら
気兼ねなく書き込んでいただければと思います。
あるいは、改善点を修正したコード全文をこちらに貼っていいただければ、本文に掲載する
コードに直接反映しないにしても、本文中にて紹介という形にさせていただくこともできます。
それでは良いお年を。
「二分木を使った数式の逆ポーランド記法化と計算」のコードを修正していただき、どうも有り難う御座います。
今までとは趣きが大きく変わりましたので正直なところ驚きました。
本格的に書き直していただきましたので、今一度、細部に渡って確認させていただきました。
致命的な不具合は見つかりませんでしたが、気になった箇所が幾つかありましたので、それらを列挙させていただきます。
どうでも良い点ばかりかも知れませんが、多少なりとも何かのヒントにでも繋がれば幸です。
get_pos_operator関数(size_t型)の戻り値が-1になる場合がある。
処理系によってはNG?
size_tは通常unsigned型で定義されるので、ゼロ以下の値は避けた方が良い。
この関数中のpos_operator変数(size_t型)も同様。
この関数の呼びだし元も見直した方がよいかも知れません。
C言語の場合はchar型のポインタで書き換えた方がスッキリすると思います。
ポインタ型が使えない他言語とアルゴリズムを揃えておきたい場合は、size_tをint型やlong型で置き換えるとよいかも。
この関数内では演算子の最低の優先度をあらわす変数priority_currentを4で初期化していますが、
対応する演算子を増やすなどした場合、この値を変更するのを忘れると不具合の原因になりえます。
4よりも大きな、できるだけ大きな整数値で初期化した方が良いと思います。
以下のコードの中で★印で囲まれたコメントは、私が追記したモノです。
==========================================================================
size_t get_pos_operator(char *exp) // ★ size_t型の関数の戻り値に-1? ★
{
==========================================================================
calculate関数内で子ノードのポインタにNULLを代入している次の2行は不要?
==========================================================================
int calculate(Node* node)
==========================================================================
remove_outer_most_bracket関数の先頭部分が冗長で少々難解。次の2点で簡素化可能。
この関数は何度も呼び出されるので、できるだけ先頭部の処理を軽くしておきたい。
丸括弧の対応を厳密にチェックする関数を別途用意して、数式入力直後に1度だけチェックするようにすれば、この関数の動作を簡素化できる。
この関数の目的を「式全体が括弧で括られている場合の処理のみ」に限定した方がよい。
特にこの関数の先頭付近でのstrlenは止めた方がよい。
==========================================================================
int remove_outer_most_bracket(char *exp)
{
// ★ この関数は重いので、旧バージョンのように必要な場合のみ呼び出した方が良い ★
// ★ 例えば、次のように変更 ★
// ★ if ( exp[0] == '(' && exp[i-1] == ')' { ★
// ★ if ( remove_outer_most_bracket(exp) < 0 ) return -1; ★
// ★ } ★
// ★ return 0; ★
==========================================================================
parse_expression関数内で使われているmemsetは避けた方がよい?
計算式が長大にると遅くなる。
==========================================================================
int parse_expression(Node* node)
{
==========================================================================
3つのtraverse関数の出力結果が見難いので、各式の間に空白等の区切り文字を入れた方がよい。
区切り文字を入れないと、2桁以上の数値を計算した場合に問題あり。
==========================================================================
void traverse_postorder(Node* node)
{
}
==========================================================================
次はソースコードの書き方に関する問題なので、何らかの正解があるわけではありません。
私個人の意見としては "else if"が何度も続くコードは少々読みにくいと感じます。
次の場合のように、if文に連なるブロックが途中でreturnで抜けている場合は、
後続の条件判断は"else if"ではなく"if"文にした方がスッキリして読みやすいと思います。
==========================================================================
int parse_expression(Node* node)
{
==========================================================================
その他
任意演算のアルゴリズムは処理速度を要求されるインタープリタの一部として使用されるケースもあると思います。
多少は速度を意識した書き方にしておいた方が、多くの方から好感を持たれると思います。
それでは良いお年をお迎えください。
>>45
遅くなりましたが、ご指摘いただいた件を含め、コードの不備を修正しました。
全体的なアルゴリズムそのものは変更していませんが、remove_bracket関数の処理が変わったほか、
calculate関数等にも変更を施しています。
そのため、コード全体で変更箇所が多くなっている点についてはご了承ください。
変更点の詳細については下記の更新情報をご参照ください。
http://smdn.jp/programming/tips/polish/#DocumentLog
問題点のご報告ありがとうございました。
>>45
ご指摘のとおり、再帰的に呼び出したremove_bracketの結果が返されず常に0となるため、現在の実装には問題があります。
(本来return remove_bracket(exp);
とすべきところでreturn
の記述が抜けていました)
ただこの問題以前に、そもそも正しく開いていない/閉じていないカッコの扱い自体に不備が多いため、
それも含めて修正したいと思います。
修正次第あらためてお知らせしますが、もしカッコの処理(remove_bracket)以外で不備が見つかりましたら
遠慮無くご報告いただければと思います。
なお現在の実装での動作です。 いずれもエラーとなるよう修正したいと思います。
2+3)
→左項2
・演算子+
・右項3)
と解釈される(2+3
→演算子なしの単項(2+3
と解釈される1+(2+3))
→左項1
・演算子+
・右項(2+3))
と解釈される1+((2+3)
→エラーとなる(意図した動作) unbalanced bracket: ((3+2)
>>44
「二分木を使った数式の逆ポーランド記法化と計算」のコードを修正していただき、どうもありがとうございます。
二度手間で大変申し訳ありませんが、気になる箇所をもう1つ見つけてしまいました。
計算式から括弧( )を取り除くための関数remove_bracketの最後の部分です。自分自身を再帰呼びだしした直後にreturn 0で正常終了しています。具体的には次の3行です。
この書き方だと、最後から2行目のremove_bracket(exp)がエラーコード(-1)を返したとしても、必ず次の行のreturn 0が実行され、呼びだし元にはエラーの発生は伝わらないと思われます。
細かなこと恐縮ですが、ご確認いただければ幸です。
>>43
問題のコードについて、strncpyが終端文字を書き込まない場合があることを把握していませんでした。
コードを修正し、終端文字で終端させるよう記述を変更しました。
なお、読みやすさを考慮してmemsetでバッファ全体をゼロクリアしてからstrncpyする方法を
採らせていただきました。
記事をお読みいただき、またご指摘いただきありがとうございました。
1>>
「二分木を使った数式の逆ポーランド記法化と計算」のページを拝見しました。
難解なアルゴリズムがわかりやすく簡潔に解説されていてとても肝銘を受けました。すばらしいです!!
このページのC言語のソースコードを読んでいて、strncpyの使い方が気になったのでご報告します。具体的には次のような行です。
strncpy(node->left->exp, node->exp, pos_op);
strncpyはコピー先に行末コードを書き込みませんので、次のような記述を追加した方が良いかも知れません。
node->left->exp[pos_op] = '\0';
なおstrncpyはrightのnode用に使われており、計2箇所あります。
私の勘違いかも知れませんが、よろしくお願い致します。
>>41
32bitということは./autogen.shで'--host=i686-w64-mingw32'を指定したものかと思いますが、
だとするとビルド成果物のMonoランタイムはWin32 APIに依存するものになっているので、
Windows以外では動作しません。
Windows上でビルドしたものをMacOSやLinuxにインストールするのであれば、その環境向けに
クロスコンパイルする必要がありますが、実際にVisual StudioとMonoのソースでそういったビルドが
できるのかはわかりません。
windowsでソースを取得してここのサイト(http://www.mono-project.com/docs/compiling-mono/windows/)を見ながらVisualStudioを使ってMONO(32bit版)をビルドしたんだけど、これはwindowsだけでなくMacOSやLinuxでもインストールできるものか教えて欲しい
ありがとう
>>37
追記です。
安定版はパッケージが用意されますが、開発中の最新の状態を使いたい場合はパッケージがない
ことがほとんどなので、そういった場合も最新のソースを取得してビルドすることになります。
>>37
例えば、インストールするディレクトリを自分で決めたいとか、コンパイルオプション等を指定してビルドしたいとか、
あるいはソースを修正しながらデバッグやテストをする必要があるとか、そういった目的がある場合は
自分でソースからビルドするという選択になると思います。
逆にhttp://www.mono-project.com/download/で紹介されているパッケージの場合はビルド済みのものを
インストールすることになるので、上記のようなことはできませんが、特にそういう目的に使う予定がなく、
とりあえず動作する環境が用意できればよいのであれば、パッケージからインストールするという選択になると思います。
回答をありがとう。35で質問した者です。
自分は、MONOの圧縮ファイルを解凍してビルドしたんだけど、自分でビルドしたものをインストールするのと、ここで(http://www.mono-project.com/download/)ダウンロードしたものをインストールするのでは何か意味や目的が違うのか教えてほしい
>>35
例えばgit cloneで/home/user/mono/にクローンした場合なら、/home/user/mono/mcs/class/lib/monolite/になります。
mcs/class/lib/のディレクトリはソースツリーの中に存在しているはずなので、そこにmonoliteディレクトリを作成して、
バイナリをコピーしてくればよいことになります。
ただ注意事項としても書いているように現在のバージョンでも機能するかはわかりませんし、System.Core.dllなど
ほかに必要になるアセンブリもあるかもしれません。
Mono のビルド・インストール(下のURL)で、MonoLiteが入手できない場合は、以下の5つのバイナリを別の環境からコピーするなどして手動で../mcs/class/lib/monolite/に配置する。と書かれているんだけど、具体的にmonoliteフォルダーを含むmcsフォルダーはどこのディレクトリーに配置すればコマンドとして機能するのか教えて欲しい。
http://smdn.jp/programming/mono/build_install/mono_from_git-master/
ご報告ありがとうございます。
検証したところご指摘のC#コードには文法上の問題はないことを確認しております。
「ファイル・ディレクトリの操作」のサンプルソースには下記の構文エラーが発生したので、ご連絡させていただきます。
URL:
http://smdn.jp/programming/netfx/filesystem/1_filesystem/
ーーーーーーーーーー
ーーーーーーーーーー
今でも自分は引っ込み思案なところがあるようで、バグ報告を躊躇う。
何人かにちょろっと話題を出したりすることはあっても(ここに書くのもその一環)、自分ではあまり報告しないのだ。【以下、無関係な話が続くので最後の1行までジャンプ】
LibreOfficeにはStarBasicというマクロ用の言語がついており、COMっぽい仕掛けを多用すること以外の文法的なことはVBA/VBSに似せようと(もともとBasic系共通なのかもしれないが)している気がする。文法書ないんだけどね。
VBAで#を使うケースで俺がポンと出てくるのが、
の3つだ。
さて、StarBasicでこんなコードを書いてみよう。
Sub Main()
Dim x As Date
Dim y As Date
x = #1/2/2013#
y = 1/2/2013
Msgbox(x <> y)
End Sub
Dateが日数単位のDoubleならば、当然x > 1かつ、yは単なる割り算だからy < 1でこの二つが一致するわけがないし、実際VBAではMsgboxはTrueを表示する。(つまり期待通り等しくない)
StarBasicではFalseである。
コンパイラのソース見てみようか。
http://opengrok.libreoffice.org/xref/core/basic/source/comp/scanner.cxx#406
これはそれほどおかしいと思わなかった。
http://opengrok.libreoffice.org/xref/core/basic/source/comp/scanner.cxx#242
bHashって何のためにあるんだろうねー。
http://opengrok.libreoffice.org/xref/core/basic/source/comp/scanner.cxx#481
VBAのリテラルって#で囲まれているんだよね…文字列ルーチンを再利用してしまおうか…何か上の方にあるelse ifあたりの条件足りなくないか?
と、ここまでが無関係な発端。いやmonoのvbncはどう解釈するのかなって話が(凄く短いけど)メイン。
http://ideone.com/HYXMKw
このエラーはどうなんだろう。vbcは演算子が定義されていないというが
>>30
BinaryReader.ReadBytesメソッドの動作についてですが、ご指摘頂いた通り
記述されている内容が誤りがありました。
正しくは「ReadBytesメソッドではEndOfStreamExceptionがスローされることはなく、
実際に読み込めた分の長さのバイト配列が返される」となります。
従って、ReadBytesを呼び出す場合はEndOfStreamExceptionがCatchされることはありません。
ファイル末尾に達したかどうかの判断は、ReadBytesが返す配列の長さが
引数で指定したバイト数よりも少ないかどうかで調べることができます。
当該ページの記載内容も修正いたしました。 ご指摘ありがとうございました。
はじめまして。
VisualBASICを使ってバイナリでファイルの入出力を行うサンプルなどを探していて、こちらのサイトにたどり着きました。
説明も解りやすく、サンプルも簡潔で非常に助かりました。
ただ一点どうも思った動作にならないので、自分で別なサンプルを作成したり、MSDNを読んだりしたところ、間違っているようなのでこちらに記載させて頂きます。
BinaryReaderクラス・BinaryWriterクラス
http://smdn.jp/programming/netfx/stream/3_binaryreader_binarywriter/
の中のバイト配列の読み込み (ReadBytes, Read)には
「ReadInt32等のメソッドと同様、ReadBytesメソッドでストリームの残りバイト数よりも多いバイト数を読み込もうとした場合には、例外EndOfStreamExceptionがスローされます。」
と記載がありますが、スローされませんでした。
このため自分が作成したサンプルで、Try Catchとして、ファイル末尾を判断して処理するようにしましたが、Catchされませんでした。
MSDNのBinaryReader.ReadBytes メソッドにも
http://msdn.microsoft.com/ja-jp/library/system.io.binaryreader.readbytes%28v=vs.95%29.aspx
例外の中にEndOfStreamExceptionの記述がありませんでした。
よろしくおねがいします。
今後もいろいろと参考にさせて頂きます。
ありがとうございます。
どうやら俺はまた誤診をしたようだ。
上記は単に、ファイルを壊した結果アセンブリとして認識されず、当然アドインとして認識されない結果としてエラーが表示されなくなっただけだった。
「エラーメッセージが間違っている」までは正しいが、
SharpSvn.dllはVC9のCRTを参照しているため、VS2008 Redist SP1が正しい。
保守。
Xamarin Studio 4.0が公開されたため、インストールしてみたのだが、Version Controlメニューを選択するとエラーが出て、かつ、ダイアログのボタンをクリックすると、IDEが落ちる現象に遭遇した。
表示通りVS 2005 Redist SP1を入れても治らず。
エラーメッセージをググったところ、どうやらエラー表示箇所はこいつらしい
メンバ変数installErrorを変更しているのが、どうやらここだけっぽい。
SvnClientコンストラクタでエラーが出ているわけか。
例外起きたら何でもかんでもこの解決策を掲示されるのはどうかと思う。まぁそういう俺も対処があるのかは知らないまま投げっぱなしだけど。
ここで、Xamarin Studio 4.0にログ機能があることにようやく気づく。
何々SharpSvn.dllが見つからない。csprojとかslnとか、あるいはバイナリを見る形で見つけられるか。
バイナリ中に以下の記述を発見。
一応このファイルの書式を調べてみた。
どうもアセンブリとfileは異なるものであるようなので、Stirlingで挿入して修正。
機能が使えるかは試していないが、とりあえず落ちることはなくなったようだ。
まぁ何かもう既にバグレポとか出てそうだけど、探すのが面倒。
>>25
vbncは確か4.5が含まれるようになる前から既にMicrosoft.VisualBasic.dllが無いって言うようになってた気がしますね。
どうでもいいが個人的にWikiの記法って嫌いだ。
すみません、使い慣れてる書式なのでこの掲示板でも使ってる次第です。
書式無しで書き込めたほうがいいですか?
そのうちにでも書式選べるようにしてみます。
何か重要なこと言ってない気がする。
補足というか本題冒頭への追加というか:
Microsoft.VisualBasic.dllをMonoのgacの4.0のディレクトリから4.5用のディレクトリ(こっちにはこれがない)にコピーすればコンパイルできることから4.0ではなく4.5のディレクトリを読みに行き、同ディレクトリ内のMicrosoft.VisualBasic.dllを探しているものと考えた。そこで、読み込むディレクトリを決定している部分はどこかという発想に至った。
あるとき(Mono 2.11.1,2012-04-23)から、Mono最新版にくっついてくるvbncで、
libpathとか指定なしで、vbのコードをコンパイルしようとするとMicrosoft.VisualBasic.dllが無い!って言ってくるようになったんだ。
●しばらく調査を面倒臭がっていた。
C:\Program Files\Mono\v2.11.4\bin
C:\Program Files\Mono\v2.11.4\lib
C:\Program Files\Mono\v2.11.4
の3つをPATHに放り込んでるから、vbnc.batで実験してた可能性大だが、MonoDevelopなしでも再現する。
また、
vbnc.batの中身を
@"C:\PROGRA~1\Mono\V211~1.4\bin\mono.exe" %MONO_OPTIONS% "C:\PROGRA~1\Mono\V211~1.4\lib\mono\4.5\vbnc.exe" %*
から、
@"C:\PROGRA~1\Mono\V211~1.4\bin\mono.exe" %MONO_OPTIONS% "C:\PROGRA~1\Mono\V211~1.4\lib\mono\4.0\vbnc.exe" %*
とか書き換えてもまだ「ない」とか言ってきた。
vbncのコードは、一部ライブラリを除きVB.NETで書かれているので、MSのvbcを使って.NET 4.0向けにビルドとかやってみたけど無駄だった。
●MfAもPSSuiteも使ったことないけど。
上記のように色々弄った状態ではあるが、
https://github.com/mono/mono-basic/blob/master/vbnc/vbnc/source/General/Compiler.vb#L119
あたりがその辺の情報を持ってそうと考え、
https://github.com/mono/mono-basic/blob/master/vbnc/vbnc/source/General/Compiler.vb#L467
LibPathを参照してそうなところがここしかなく、ここを見る
https://github.com/mono/mono-basic/blob/master/vbnc/vbnc/source/General/Compiler.vb#L989
こう大雑把に書いてログ取り。
●try catch finallyとかusingどこ行った>俺
出力結果:
pass
C:\PROGRA~1\Mono\V211~1.4\lib\mono\4.0\System.Core.dll
C:\PROGRA~1\Mono\V211~1.4\lib\mono\4.0\System.Xml.dll
C:\PROGRA~1\Mono\V211~1.4\lib\mono\4.0\System.Configuration.dll
C:\PROGRA~1\Mono\V211~1.4\lib\mono\4.0\I18N.CJK.dll
C:\PROGRA~1\Mono\V211~1.4\lib\mono\4.0\I18N.dll
C:\PROGRA~1\Mono\V211~1.4\lib\mono\4.0\System.dll
C:\PROGRA~1\Mono\V211~1.4\lib\mono\4.0\Mono.Cecil.VB.dll
C:\PROGRA~1\Mono\V211~1.4\lib\mono\4.0\Microsoft.VisualBasic.dll
C:\PROGRA~1\Mono\V211~1.4\lib\mono\4.0\vbnc.exe
C:\PROGRA~1\Mono\V211~1.4\lib\mono\4.5\mscorlib.dll
何で最後だけ4.5なんだよ(怒)
「AppDomainのソースコードから呼び出されるCのコードはバージョン非依存になってそうな気がする」と勘を働かせ、Cに慣れてないこともありそこから逃避。
ただ、納得がいかず、うんうん唸りながら検索を掛けて一つの仮説を思いつくことになる
MS .NET 4.5って、
http://www.west-wind.com/weblog/posts/2012/Mar/13/NET-45-is-an-inplace-replacement-for-NET-40
MS .NET 4.0を置き換えちゃうんだな。
http://atsushieno.hatenablog.com/entry/2012/06/14/141356
の件があって、RTMを待って入れている俺の環境でもそうなっている。
(8月21日でも、当時結構RCとか紛らわしい状態だった。以上のことは俺がどこも間違えてない前提。)
MSのコンパイラvbcは、ビルドしたアセンブリを4.5扱いにするんじゃね?
で、公式のビルドマシンもそうなっているから、今回のトラブルが発生しているんじゃないか?
ってのが自分なりの今の仮説。
…まだやってないけど、.NET 4に戻せば治っちゃったりして。
バージョンを下げるのって負けた気がするからあまりやりたくないのだが…(いつ検証するか不明。しないかも)
ふと思ったこと。
●エンドユーザーへの説明・影響
●テスト体制・リリースマネジメントの改善
=======================
どうでもいいが個人的にWikiの記法って嫌いだ。コメント扱いのつもりでアスタリスクやナンバーサインを書くとorz
殴り書き
何度でも繰り返す。「俺は一体掲示板を何だと考えているのだ」と。
makefileを読んだりしていないけれど、Mono,GTK#,MonoDevelopのWindows版公式ビルドはどのようにビルドされているのだろう。
使っているのがMSのコンパイラ + MS.NET Frameworkである気がしてる。
==============【以下しばらく読まなくてもいい】================
Mono上で4.0向けGTK#アプリを作ったところ(実際にやったのはMonoDevelopのslnのビルド)、問題が二点あった。
●バックアップとるとか、あるいはMonoとGTK#の再インストールとか覚悟してね。
●各ライブラリはgithubからzipファイルへのリンクをクリックしてmasterの最新版をダウンロードして展開して参照している。
いい加減バージョン管理ソフトだのビルドツールくらい、コマンドライン慣れろよ>俺
●なお、MonoDevelopはそのソース中、拡張メソッドを使っているので.NET 2.0向けのビルドは不可。
3.5は知らんが、確か参照しているプロジェクトが何か.NET 4.0用のライブラリ・クラスを使っていた気がするのでたぶん無理じゃないかなー。
一つ目。
Mono側に入っているlibgtk-win32-2.0-0.dllのバージョンは2.16.6で、Windows版GTK#についてくるのは2.24とかその辺のバージョンなので、
Mono側から持ってきたファイルで上書きします。(なぜかそうしないとコケる。)
二つ目。
Windows版 GTK#インストーラでは、MS .NET 2.0向けにMono.Cairo.dll,Mono.Posix.dllがインストールされる。
MonoのインストーラではMonoのgacに2.0用と4.0用のMono.Cairo.dll,Mono.Posix.dllがインストールされる。
MonoDevelopをMonoでv4.0用にビルドすると、gacから.NET 4.0のものが引かれる。
できたアセンブリを、Windows上の.NET上で動かそうとすると4.0用のライブラリがないのでコケる。
まぁしょうがないから、Monoからバイナリをコピーしてmonodevelop.exeのディレクトリに入れて…一応動くはず。
●なんかメモによると、internalとかprivateになっているやつをpublicにしないと、MonoDevelopで定義場所探したら何か他のプロジェクトにある同名クラス(っていうか同じ仕組みで生成されるファイル)が参照されていて謎とかあった。当時(2012-06-24)のことあまり思い出せなくて、それでコケたかは忘れた。
●https://github.com/mono/monodevelop/blob/master/main/src/addins/MonoDevelop.GtkCore/libsteticui/CodeGenerator.cs#L34
●…とまぁ落とし穴が沢山あるので、
http://atsushieno.hatenablog.com/entry/2012/08/27/130305
を知った時、俺はこっそり色々心配してたとか。(「余計なお世話」なの自覚しているので「こっそりと」)
●Linuxサイドからクロスプラットフォームの世界を見せるなら、v4.0向けの書き方やライブラリ禁止ってことだね。
●まぁ、Monoでのビルドをデフォルトにしている偏屈な俺と違って、
普通なら、WindowsではMS .NET Frameworkを使うだろうし、作った環境で動かすだけならトラブルにはならないだろうから、
過剰な考えなんだろうなーと。
●「多分.NET 2.0より後でGTK#がビルドできないのはMono.PosixやMono.Cairoが提供できないから」って点も
●「MSのコンパイラを使っている」説の傍証の一つになっている。俺の中では。
==================【ここから本題】======================
と、ここまでが前座。気づいた時の経緯の一つで、実は殆ど本題には関係ない(笑)。
「公式ビルドはMSのコンパイラを使っている」と俺が考えている、ってことが大事。
保守
例によって役に立たない挫折日記である。まったく俺は掲示板というものを何だと思っているのやら。
ネタバレ: 最後は挫折。
俺はVista + MonoDevelop 2.9.2という環境で使っている。
先日、Mono 2.11が出たというので、手元の2.10.8と入れ替えることに。
インストール自体は特に問題なく終わった。
以前提出したバグレポは、2.10には入っていないが、2.12の前座であるこのバージョンなら
この修正が入っているかもしれないと思い、試す。
Bug 678357 - Setting System.Console.OutputEncoding should change System.Console.Out.Encoding
https://bugzilla.novell.com/show_bug.cgi?id=678357
MonoDevelopでさくっとコードを書いて、コンパイル…出来ないorz
MonoDevelopはこんなふうにフレームワークのバージョンによって使うコンパイラの名称を場合分けして
https://github.com/mono/monodevelop/blob/master/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Assemblies/MonoFrameworkBackend.cs#L108
でこうやってMonoのインストール先のbinディレクトリからdmcs.batを探すの。
https://github.com/mono/monodevelop/blob/master/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Assemblies/TargetFrameworkBackend.cs#L69
ただ、Mono 2.11はこんな風にわかれてない。4.5のコンパイラを利用するmcs.batしかないので、dmcsという拡張子なしのファイルを呼び出そうとしてコンパイルに失敗する。
MonoDevelopのこの部分のコードを治すのはちょっと面倒かな、と思ったので後回しにして、mcs.batをコピーしてdmcs.batに名称を変更する。
コンパイル成功。上記バグは治ってることを確認できた。
余談。
俺はあまり開発を理解していないようだ。躊躇いがあるというかなんというか。
以前、OpenOffice.orgに対してバグ報告した際にも、「これ、俺がやらないといけないことなのか?それとも専門のQAチームに任せなければならないのか?」と、まごついてコメントしている。
正直、現在も自分の立ち位置がわかっていない。批判を少し怖がっているようなところがあって、あまり相談することもないからね。
https://issues.apache.org/ooo/show_bug.cgi?id=105165#c7
上記のMonoのバグに対しても、「テストされれば、2.10にバックポートできるよ」というその「テスト」を「俺がやるべき」だったのか自信が持てず、結局返答を控えることになってしまっている。無駄に引っ掻き回して迷惑になりたくもない。
閑話休題。上記のようにSystem.Console.OutEncodingをUTF-8に設定(BOMを出力しないほうが見やすいが)した時、
System.Console.WriteLine("あ");と入力した時に「アプリケーション出力」に出力される結果は
縺・
UTF-8で「あ」を表した時の3バイトを日本語版Windowsの「Encoding.DefaultであるShift_JISで解釈した」とすると、最初の2バイトが縺で、ラスト1バイトを表すことができずにU+30FBに置き換える羽目になったわけだ。
つまり、このアプリケーション出力では、Unicodeにしか含まれないU+2704等の文字を表示できないってことだ。
イチイチファイルに出力するのも面倒だし。コードページ65001に変更したコマンドプロンプトから起動ってのもなあ…。
…結構前からこれは気になっていて(調査はしてなかった)、バグ報告(という名の丸投げ)しているのだが、皆の関心は殆どないようで、進展は特になさそうだ。
Bug 680024 - Let me change Encoding for OutputPad
https://bugzilla.novell.com/show_bug.cgi?id=680024
丸投げするだけじゃ怒られそうな気がしたため(誰にw?)、少しコードを追ってみることにしたってわけだ。
自信はないが、ここじゃないかなーと思う。もしここで、StandardOutputEncodingを弄ってUTF-8を代入していればなんとかなるんじゃないか。
https://github.com/mono/monodevelop/blob/master/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Execution/ProcessService.cs#L198
仮説は実際に動かしてみるまで飽くまで仮説。
早速、MonoDevelopでMonoDevelopをビルドすることに。「今まで何度かやってきたから、出来るだろう」などとこの地点では高をくくっていたのだが…。
Mono.AddinsのソースとMonoDevelopのソースをダウンロードして、MonoDevelopの中でMono.Addinsの各プロジェクトへの参照として追加する。
あ、ついでに各プロジェクトを.NET 4.0を対象にしなおしておく。何となく上のバージョンのほうが落ち着く。いつものことだ(おぃ
コンパイルが確率でしか成功しない。しかも失敗するプロジェクトがその時によって違うorz何でだろう。
試行錯誤すると、たまに成功するんだけど…
自分でビルドしたMonoDevelopはMSの実装上ではウィンドウ自体が表示されないので(いつものこと。理由不明。もしかしたらgacにライブラリ突っ込んでおく必要があったりするのかなあとかボンヤリ考えている)
コマンドプロンプトからmono上で起動…今回は今までになくスプラッシュスクリーンでハングしたりlibgtk-win32-2.0-0.dllが原因のクラッシュが発生する。
これまたイヤなことに確率でどっちになるかが別れる。
警告減らすか。えーっとCS1701はMono.Cairoで2.0のライブラリと4.0のライブラリがあって、どっち呼び出すのかわからない、か。
自信はないけど、確かに、参照設定しているライブラリ(どれか忘れた)が、Mono.Cairo 2.0を参照しているようだ。対する俺はさっき、4.0にしたばかり。
もしかしたら、それが原因かもしれない。ただ、そのままのバージョンじゃビルドできないやつ確かあったしなあ。
対処はっと…えー、こんな面倒くさいコードに書き換えて、コンパイルオプション書かなきゃいけないの?
http://msdn.microsoft.com/en-us/library/2h4x8b08%28v=vs.80%29.aspx
ええい、面倒くさいっ!GTK#ごとビルドしてやるっ!
http://www.mono-project.com/Compiling_GtkSharp
Building Gtk# 2.12 with .net >= 2.0 is known not to work.
泣きたい…。
前回の解決をしないまま(面倒くさがりなので今後もしないだろう)
今回はちょっと傾向が異なる話。
発端は「VB.NETで出来ることは、殆どC#で出来るはず…だよね。」と思ったこと。
VBは結構特有の構文があるので、考えてみたわけだ。
脱線 - 昔のVBの構文に無理やり当てはめたC#と誰かが言っているのを見かけた。
構文が美しいのがC#かどうかは紛糾するからおいておくとして
http://logsoku.com/thread/tsushima.2ch.net/news/1253599691/#400
→Declare構文はどうだろう?vbncのソースを追ってみよう!
https://github.com/mono/mono-basic/blob/master/vbnc/vbnc/source/Members/ExternalSubDeclaration.vb#L97
なるほど。DllImportAttributeをくっつけたものに対する糖衣構文だったわけだ。
じゃあ、C#に同じものがない、とされる文字列同士のLike演算子はどうなっているのかな、と思って追ってみた。
http://dobon.net/vb/dotnet/vb2cs/vblike.html
Microsoft.VisualBasic.CompilerServices.LikeOperatorだの
Microsoft.VisualBasic.CompilerServices.Operatorsだの飛ばされるけど、
Microsoft.VisualBasic.CompilerServices.StringTypeのStrLikeメソッドが本体であるようだった。
なお、こいつらは、MSDNにも載っているから、一応使おうと思えばC#から使うことができないこともない。
ただ、あくまでフレームワーク側をサポートするものであり、消えたり実装の内容が変わったりすることもあるかもしれない。
また、
http://en.sourceforge.jp/forum/forum.php?thread_id=26779&forum_id=15322
こういうことを報告した身としてはあまり使わせる気にはならない。
ConvertLikeExpressionで、正規表現を用いた実装に切り替えている。
https://github.com/mono/mono-basic/blob/master/vbruntime/Microsoft.VisualBasic/Microsoft.VisualBasic.CompilerServices/StringType.vb#L239
え?この実装ちょっと待て。
MSのコンパイラ+ライブラリ→True
Monoのコンパイラ+ライブラリ→False
やっぱりなorz
本家に報告するかはわからん。気分屋なのでね
私も興味ありますし、こういった内容もいいかなと思っているので、書き込んで頂いて大丈夫です。
検証してある程度見当がついたら気が抜けてしまい、
後で見返したら自分がどうしてそういうコードだと理解できたのかわからずしばらく悩んだ。
多分、HandleCodeCompletionはCompletionTextEditorExtension.KeyPressから来たんだと思う。
その後、継承先であるCSharpTextEditorCompletion.HandleParameterCompletionが呼び出され、
その中で
NRefactoryParameterDataProviderのコンストラクタのオーバーロードである
public NRefactoryParameterDataProvider (TextEditorData editor, NRefactoryResolver resolver, IType type)
が呼ばれる。何故かコンストラクタを探すためのコードのはずなのにInvokeメソッドを探していたので、
等と実験的に変更してみるも、どうやらコンストラクタとなるメソッドが存在せず、実際にビルドして使ってみるとうまくいかなかった。
そこで、ITypeとなる引数を調べてみることにした。
このあたりで、MSDNには該当するInvokeメソッドなんか載ってないことを疑問に思った俺は検索を掛けたところ、
コンパイラが勝手に足す、という情報を得ることが出来た。
http://www.codeproject.com/KB/cs/delegatesneventsinternals.aspx
さて、IType typeはresolver.SearchTypeの戻り値であり、これは結局、ソースコードのパースで得られた型の集まりから、
該当する型を取ってくるものであるから、その生成過程がおかしいと考え、BeginInvokeやInvokeで検索し、その正体がDomTypeであることを突き止める。
https://github.com/mono/monodevelop/blob/master/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Dom/DomType.cs#L460
確かにInvokeしか追加されていない。
そこで
とか実験的に足してあげたところ、確かに、ビルドして試すと引数なしのコンストラクタの情報が掲示されるようになった。
==============-
自分が今のところわかっていないのは、
一体メソッド名の型は何としたらいいのだろう?ということ。2引数のコンストラクタを定義したらうまくいくなどということもないだろうと思う。
あるいはコンストラクタとしてとらえるのをやめなければならないのだろうか?
ということである。
====
話題的に大丈夫かなあ、自分ではと思って書き込んでみたけれど、失礼だったり、邪魔だと感じる投稿であればそちらで消していただけると幸いです。
俺はラムダ式の書式に抵抗感を覚えるようで、
まだ
という匿名関数の形を良く使っている。
ただ、どうも打ち間違いが多いと思ったら、MonoDevelop 2.8でコードを書いていると、変数iが補完リストに出てこなかったのだ。
これと同じだね。(調査中の過程だったんだ、俺のコメント2は)
http://bugzilla.xamarin.com/show_bug.cgi?id=1132
で、ソースコードを追い始めた。
何かSystem.Console.WriteLineでコンソールに出力できなくって、
ファイルに書き出すコードを突っ込みまくって調べた。
どうやら、ファイルを変更するたび、ソースコードの構文木を作り、
Visitorパターンで最上位から流れるように追っかけるようだ。
ICSharpCode.OldNRefactory.Visitors.LookupTableVisitorは恐らく、変数などのidentifierを集めてAddVariableで変数の型、変数名、宣言自体の位置、変数使用可能範囲等を登録する。
たとえば、変数宣言でいうと、
最初は変数の型、次が変数名、次が、変数の宣言開始位置、次が変数の使用終了位置(おそらくenclosing blockの終わり)、次が修飾子で、
変数使用可能な範囲の始まり(補完リストに追加する地点:inListPosition)が、「セミコロンの後から」
これがラムダ式なら、
変数使用可能な範囲の始まりは、そのラムダ式の始まり、というわけだ。
これを踏まえて匿名関数のほうを見ると、
変数使用可能な範囲が
その「匿名関数の終わり」から「匿名関数の終わり」になっている。これは誤りだ。だから以下のように修正する。
===
CSharpBindingプロジェクトのMonoDevelop.CSharp.Completion.CSharpTextEditorCompletion.HandleCodeCompletion
から615行目default:へ流れて、636行目CreateCtrlSpaceCompletionDataの呼び出しがあり、
1961行目CreateCtrlSpaceCompletionDataの初っ端でこのLookupTableVisitorをメンバに持つ、NRefactoryResolverの設定を行う。
ここで、VisitCompilationUnitが行われている。同時にカーソルの位置も記録。
expressionResult.ExpressionContext == ExpressionContext.Defaultを満たして呼ばれるresolver.AddAccessibleCodeCompletionDataでは
カーソルの位置と、各変数の使用可能範囲であるかを確認して、その範囲内であれば、その変数を補完リストに追加する。
さっきの「匿名関数の終わり」から「匿名関数の終わり」の中にはカーソルが入らないので補完リストに加わらなかったところ、
加わるようになったので、正常に作動するようになる。
TEnumがEnumでない場合でもArgumentExceptionをスローしない。
trunk r148762のgmcsで発生。 以下のようなメソッドで警告CS3019が発生する。
PartialClass.Method3がinternal classのメソッドとみなされているため'is not visible'となる? 試しにpublicを明示的に指定すると警告が出なくなる。
WebClient.UploadStringAsyncが常にNotSupportedExceptionをスローする。
WebClient.UploadStringAsync()でSetBusy()が呼ばれたあと、UploadStringAsync()から呼ばれるUploadData()でもう一度SetBusy()が呼ばれている。
SetBusy()ではなく、CheckBusy()を呼べばOK。
フォーマットを指定しないでXmlConvert.ToDateTimeOffsetを呼んだ場合、タイムゾーンの部分が読み落とされる。
XmlConvert.ToDateTimeOffsetからDateTimeOffset.ParseExactを呼ぶときに渡されるformatsの順序が
になっているので、1でパース出来てしまうとタイムゾーンの部分が無視される。 なので、これを
の順にすれば、タイムゾーンの部分を読み落とさなくなる。
フォーマットを指定しないでXmlConvert.ToDateTimeOffsetを呼んだ場合、タイムゾーンの部分が読み落とされる。
XmlConvert.ToDateTimeOffsetからDateTimeOffset.ParseExactを呼ぶときに渡されるformatsの順序が
になっているので、1でパース出来てしまうとタイムゾーンの部分が無視される。 なので、これを
の順にすれば、タイムゾーンの部分を読み落とさなくなる。
書式文字列にzzzを指定し、かつ入力文字列のオフセット部分にDateTimeFormatInfo.TimeSeparatorが含まれない場合、FormatExceptionがスローされる。 ("+09:00"はOK, "+0900"はNG)
DateTimeFormatInfo.TimeSeparatorが含まれていない場合でも不正としないように直す。