start

C#のCreateDocumentTypeがタイムアウトする時の簡易対策

C#のXmlDocumentでHTMLを生成しようと、W3CのDTDを指定してXmlDocument.CreateDocumentType()するとタイムアウトやHTTPステータスコード500で例外を吐くことがある。こちとらvalidなHTMLを生成しようと真面目に指定してんのに、この仕打である(´・ω・`)。みんなW3Cを見に行って慢性的な高負荷状態になってるのが原因らしいが、まぁ当然そうなりますわな…。

コードにすると↓な感じ。

XmlDocument doc = new XmlDocument();
XmlDocumentType docType = doc.CreateDocumentType(
	"HTML",
	"-//W3C//DTD HTML 4.01 Frameset//EN",
	"https://www.w3.org/TR/html401/frameset.dtd",
	null); /* ここでエラー */

XmlResolverでローカルキャッシュしたDTDをマッピングするのが正攻法らしいのだが、ぶっちゃけ面倒。というか、調べても良くわからんかったのでパスw(分かったら追記する…多分)

とりあえずエラーを回避するだけなら、CreateDocumentType実行前にXmlDocument.ResolvernullにしてやればOKっぽい。コードにするまでもないが一応書いておく。

XmlDocument doc = new XmlDocument();
doc.Resolver = null; // 追加
XmlDocumentType docType = doc.CreateDocumentType(
	"HTML",
	"-//W3C//DTD HTML 4.01 Frameset//EN",
	"https://www.w3.org/TR/html401/frameset.dtd",
	null); /* エラーにならない */

・・・と、ここまで書いて思ったが、これで回避できるって事は自前実装したXMLリゾルバでDTD返してやればいいだけなんじゃね?

P4で「Client 'foo' can only be used from host 'bar'」と言われた時の対処方法

Perforceのワークスペースは基本的にマシンと紐付いているため、複数マシン間で使い回すことができない。

普通は使い回しはしないので問題はないのだが、故障などでマシンを交換した時にちょっと面倒なことになる。利用可能なワークスペース一覧に表示されず、たとえ無理やり選択しても「Client 'WORKSPACE' can only be used from host 'OLD-HOSTNAME'」と怒られて使えないのだ。

ちなみに新旧マシンのホスト名を一緒にしておけば問題は起きず、まぁ新マシンで新たにワークスペースを作ってしまうのが正攻法なんだろうけど、巨大なデポだと再取得するのも嫌じゃん? そんな時はp4 clientコマンドでワークスペースが持っているホスト名を変更すれば良い。手順は下記の通り。

  1. p4コマンドが使える状態にする。
  2. p4 client を実行。
  3. ワークスペースの情報がテキストエディタで表示されるので、Host: OLD-HOSTNAME となっているところを Host: 新マシンのホスト名 に変更して保存。
  4. テキストエディタを終了すると、Client WORKSPACE saved.と表示されワークスペース情報が更新される。
  5. 念のためp4 infoで「Client host:」が書き換わってるか確認。

以上で新マシンから既存のワークスペースが使えるようになるハズ。

C#のジェネリックで特殊化っぽいことをする

新年明けましておめでとうございます(遅。開設14年目となるクソゲ~製作所を本年もよろしくお願い致します。

Cのテンプレートには特殊化という、特定の型パラメータの時にテンプレートの実体を別に定義する機能がある。だが、C#版テンプレートとも言えるジェネリックでは、なんということでしょう、特殊化が使えないではありませんか!それをどうにかしてジェネリック特殊化っぽい事をしてみたっていうお話。 こんなコードがあったとする。 <code csharp> public class Node { // 子ノード public List<Node> Children; // 特定の子ノードを取得 public List<T> FindChildren<T>() : where Node { List<T> list = new List<T>(); Type type = typeof(T); foreach (Node e in Children) { if (type == e.GetType()) { list.Add(e); } list.AddRange(e.FindChildren<T>()); } return list; } } public class DocumentNode : Node {} public class PageNode : Node {} public class TitleNode : Node { public string Title; public TitleNode(string title) { Title = title; } } public class SectionNode : Node { public TitleNode Title; } static void Main() { DocumentNode doc = new DocumentNode(); PageNode page = new PageNode(); doc.Children.Add(page); SectionNode section1 = new SectionNode(); section1.Title = new TitleNode("はじめに"); page.Children.Add(section1); SectionNode section2 = new SectionNode(); section2.Title = new TitleNode("つぎに") page.Children.Add(section2); // 全セクションを取得 List<SectionNode> allSections = doc.FindChildren<SectionNode>(); //正しく取得できる // 全タイトルを取得 List<TitleNode> allTitles = doc.FindChildren<TitleNode>(); //★正しく取得できない!! ... } </code> <code> ◆データ構造 [DocumentNode] -Children -[PageNode] -Children -[SectionNode] -Title -Children -[SectionNode] -Title -Children </code> 文章を模したデータ構造を作り、''FindChildren''メソッドで型をパラメータに子ノードを取得しているが、 ''doc.FindChildren<SectionNode>()''は正しい挙動をするものの、''doc.FindChildren<TitleNode>()''の方は空のリストが帰ってくる。''FindChildren()''は''Node.Children''しか見てないので、独立したメンバ''SectionNode.Title''が入るはずはなく当然の挙動である。 こんな時、C++なら特殊化で''FindChildren<TitleNode>''専用の処理が書け同一のインタフェースを提供できるのだが、前述の通りジェネリックは特殊化が使えないの。C#的には''FindChildTitleNodes()''的な別メソッドで提供するのが正しいのかもしれないが、やっぱり統一感に欠けて美しくない(そもそもこんな糞いデータ構造にすんなって話だが例ってことで許してね。) で、知恵を振り絞って''FindChildren''を以下のようにした。 <code csharp> public List<T> FindChildren<T>() : where Node { List<T> list = new List<T>(); Type type = typeof(T); if (type == typeof(TitleNode)) // (1) { List<T> titles = new List<T>(); // (2)-a var sections = FindChildrenInternal<SectionNode>(); section.ForEach(e => { titles.Add(e.Title as T); }); // (3) return titles; // (2)-b } return FindChildrenInternal<T>(); } List<T> FindChildrenInternal<T>() : where Node { List<T> list = new List<T>(); Type type = typeof(T); foreach (Node e in Children) { if (type == e.GetType()) { list.Add(e); } list.AddRange(e.FindChildren<T>()); } return list; } </code> 元の''FindChildren()''を''FindChildrenInternal()''とし、''FindChildren()''には型パラメータ''T''に応じた処理を書くようにした。C++でコンパイラが自動で行ってくれる特殊化による処理の振り分けを、実行時に手動で行うような感じかしら。原理上、実行時コストは増えるが、この程度なら大した影響はないだろう。JIT様もあることだし。 型が増えると''if''と特殊化処理の嵐になるが、型ごとに処理デリゲートを作って''Type''とデリゲート辞書でも作ればすっきりするので大した問題ではない。そもそも、そんな状況は最早「特殊化」の本質から外れてるので設計から見直すべきだろう。 このコードのミソは(1)~(3)の部分。 (1)で処理すべき''T''の型が明確になったものの、(2)-aで''List<T>''としているのはコンパイルを通すため。''List<TitleNode>''でも良さそうに見えるが、''T == TitleNode''になるとは限らないので(2)-bで型不一致エラーになる。''List<TitleNode>''とした場合に''T = SectionNode''で呼び出した時にどうなるかを考えれば、すぐにおかしさに気づいて頂けるかなと。 (3)も一見''titles.Add(e.Title)''で良さそうだが、これまた''TitleNode''が必ずしも''T''に変換可能とは限らないのでCS1503エラーとなる。よって、ちょっとアホくさいが''TitleNode''を自身の型''T''にキャストしてやらなければならない。 メソッドの引数に型パラメータを持てる場面では、[[https://arbel.net/2007/11/22/c-partial-specialization-with-extension-methods/ にも軽量な型情報システムがあればいいのになぁ…。

永遠に讃えよ我が芋煮

PCのHDD整理していたら表題の歌詞が出てきた。確かtwitterで見かけて素晴らしいクオリティに感動しコピペしたものだった気がする。

今軽くググってみたらヒットしなかったので、ここにコピペして保存する。なくすには惜しい出来なので。

永遠に讃えよ我が芋煮
(山形神聖醤油帝国 国歌)

気高きは 醤油の意志
示せ あまねく宇宙に
理想貫く 味
芋の加護は 我らと共に
あり続けん
ガーレ=イモニン
誇りある 鋼の大鍋

2015年最後の日記がこれってどうなのよ。

インク商法で儲けてるプリンタメーカーは滅びればいいよね

写真コンテスト応募のため、気合いを入れてA4の高級写真用紙&純正インクを購入し最高品質で印刷してところ、たった25枚ほど印刷しただけでインク残量警告が出やがった!

プリンタはHP Photosmart 6520で購入したインクはHP178 4色マルチパック(CR281AA)。ビックカメラ店頭で3700円程だったと思う。

インク残量表示から計算すると、HP178マルチパックのカラーインクで印刷できる写真の枚数は、A4換算で多くても40枚程度と見られ1枚あたりのインク代は92.5円となる。この数値だけを見ると一概に高いとは言えないかもしれない(個人的には十分高いと思う)が、実際にはテストプリントや失敗の分、加えて紙代に超微々たるものだが電気代も掛かるわけで、かなり高コストだと言わざるを得ない。当然、プリンタ本体の代金だって掛かってるわけで…1万円で1000枚印刷したとしても10円/枚もベースコストが掛かる計算なわけで……。

ネットの激安写真プリントだとA4 1枚100円前後からやってくれる所があるし、L判なら更に安い。まだ使ったことがないので印刷品質は分からんが、家庭用インクジェットより劣るとは考え辛い。もう写真プリントは全部外注で良いかなって気がしてくる。おうちプリントは「その場で印刷できる」って事が最大のメリットだが、それすらも店頭にあるセルフプリントマシンや、最悪コンビニのマルチコピー機を使えば薄らいでしまう。

大体さー、家庭用インクジェットプリンタのインクが高いのって、本体を安く売ってインクで稼ぐという大義名分があったわけだけど、最近のプリンタは明らかに造りが安っぽくなってるよね。

2年程前かな、実家で使ってたCanon MP560が年賀状シーズン中に突然故障し、やむなくMG5530を購入する羽目になったが、兎に角造りが安っぽい。グレード的には下がった事になるが、それを差し引いても安っぽい。背面給紙はできなくなるわ(これはMG5530だけではなく最近の機種は須く非対応。普段使いのA4普通紙は底面トレイ、年賀状や本気写真印刷などその場の給紙は背面と使い分けられて超便利だったのに)、動作音は半端なく煩くなるわ(激安Photosmart 6520よりも煩い!)、肝心の印刷品質も粒状感が目立って落ちてるわ、良いところがまるでないという。当然、MP560用のインクストックは無駄になったし…。

こんなクソみたいな造りでは、本体価格の1/4〜1/2もする高価な純正インクを買おうなんて気は全く起きなくなるね。「非純正インクのせいでプリンタ本体に金を掛けられなくなった」という言い分もあるだろうが、だったら最初からインク商法なんざするなよとしか言えない。割安なインクが出てくる事なんて目に見えてるんだし、そもそも純正インクの価格設定が高すぎると思うんだよね。多くの人が高いと思っているから非純正インクの市場が成り立ってるんでしょ?

自分も昔は上位ラインの機種と律義に純正インク使ってたけど、使用頻度の割にトラブルが多過ぎて、その度に労力と金を掛けるのがバカバカしくなったため、もう格安プリンタに格安非純正インクで良いという結論に達した。ちなみに、決定打となったのはPIXUS iP7500の印刷がかすれるようになったので修理に出したら、修理の保証期限が切れた直後に症状が再発した事件があったから。その間、印刷したのは多く見積もっても10枚程度。流石にゴルァ電したけどどうにもならなかったので、金輪際キヤノン製品は買わないと心に誓った(笑)

実際に非純正インクを使ってみても、質的には何の問題もないしねー。今使ってるHP178の互換品は純正よりも発色が良かったりするし…ただし黄色は非純正の方が明らかにくすんでいたので、冒頭に書いた通り本気印刷用に純正品を買ってみたら、減るの早過ぎてこの記事を書くに至った次第。

  • start.txt
  • 最終更新: 2022-07-27 15:26
  • by Decomo