日記帳だ! with Tux on Libserver

二度目の大改造!! 日記帳…か?を継承し、より柔軟でパワフルなBlogに変身しました。

RSSに対応しています。リンク・コメント・トラックバックは自由にしていただいてほぼ問題ありません。
RSS購読方法、僕のリンク・コメント・トラックバックについての考えを読むことをおすすめします。

JavaScriptを有効にし、Cookieを受け入れ、以下のブラウザを使うことで完全なコンテンツが楽しめます。
Mozilla Firefox 3.0(Get Firefox)・Opera 9.6・Safari 3.2・Lunascape 4/5(Gecko)・Lunascape 5(WebKit)
Internet Explorer 7/8とそれを使うIEコンポーネントブラウザ(Lunascape・Sleipnirなど)

<< 過去

ビット順を入れ替えたかった

論理シミュレーションで使うメモリのデータを作って、それを使ってシミュレーションをしてみると期待値と合わない。

なにが起きてるのかと調べたらMSBとLSBが逆転していた。

どうもメモリ内部のビット順と、論理的なビット順が逆になっているようで。


というわけでメモリのデータを作る前に、ビット順を反転させる処理を足せばいいんですね。

論理シミュレーションに食わせるデータはPerlで書いていたのだが、ここにこういうコードを足した。

$a=($a&0x0F)<<4|($a&0xF0)>>4;
$a=($a&0x33)<<2|($a&0xCC)>>2;
$a=($a&0x55)<<1|($a&0xAA)>>1;

これで8ビット分の反転になる。

1行目で前4ビットと後4ビットを入れ替え、2行目で4ビット内で前2ビットと後2ビットを入れ替え、3行目で偶数ビットと奇数ビットを入れ替える。

0行目: 12345678
1行目: 56781234
2行目: 78563412
3行目: 87654321

結果として、MSBとLSBを反転することが出来る。


ビット順の入れ替えをやることはそんなに多くなさそうだが、

ビックエンディアンとリトルエンディアンでデータを行き来するときに、バイト順を入れ替えると言う処理はやる機会も多いかも。

書き方はいろいろあるんだが、上と同じように書けばこんなんかね。

a=(a&0x0000FFFF)<<16|(a&0xFFFF0000)>>16;
a=(a&0x00FF00FF)<< 8|(a&0xFF00FF00)>> 8;

こういう書き方って一般的なのかな? サンプルではあんまり見ないけど。

上のコードと組み合わせると、32ビットの反転は下記の通り書ける。

a=(a&0x0000FFFF)<<16|(a&0xFFFF0000)>>16;
a=(a&0x00FF00FF)<< 8|(a&0xFF00FF00)>> 8;
a=(a&0x0F0F0F0F)<< 4|(a&0xF0F0F0F0)>> 4;
a=(a&0x33333333)<< 2|(a&0xCCCCCCCC)>> 2;
a=(a&0x55555555)<< 1|(a&0xAAAAAAAA)>> 1;

プログラムで書くとなんとなくめんどくさいが、得られるものはただ単にビット順を入れ替えたもの。

回路で言えば結線を変えるだけ。計算とも言えない操作だ。

というわけで、CPUはバイト順・ビット順を入れ替える専用の命令を持ってることが普通だ。

ARMだと REV命令 でバイト順を反転でき、RBIT命令でビット順を反転できる。x86だと BSWAP命令 でバイト順を反転できる。

パフォーマンス重視ならそういう専用の命令を使えた方が便利だよね。

ただ、プログラムの中でちょっと組み込むのにアセンブラ呼び出したりするのは割に合わないしね。

上のような記述法の出番はそこそこありそう。


自分は上のように書いたのだけど、後で別の目的で同じような処理をしているコードがあって、それを見たら、

a=(a&0x80)>>7|(a&0x40)>>5|(a&0x20)>>3|(a&0x10)>>1|
  (a&0x08)<<1|(a&0x04)<<3|(a&0x02)<<5|(a&0x01)<<7;

安直だなぁと思ったけど、やりたいことってそういうことだよね。

どちらもパフォーマンスはどうでもいい用途なので、別にどういう書き方でもいいんだが、

単純に考えれば、最初に僕が書いた書き方の方がかなり効率はよさそう。4ビット単位に拡張したりできるメリットもあるし。


Author : hidemaro
Date : 2017/07/24(Mon) 19:42
C・C++ | Comment | trackback (0)

ツールにツッコミを入れられた

先日、プログラムを一部修正したのだが、そのソースコードの記述についてチームリーダーから指摘が入った。

意味は変わらないだろうけど、こう書いたほうがいいし、こう書かないと解析ツールに指摘されるんじゃないかという話だった。

ごもっともな話だったので、修正しておいた。コンパイル結果のバイナリは全く一緒だったけど。


他の部分は大丈夫? ということで気になって部署で使ってる解析ツールで解析してみた。

するといろいろ引っかかるのがあるのだが、全ての指摘事項を消し去る必要は必ずしもない。

「この記述方法は注意が必要ですよ」ぐらいの意味なので、「これで正しいし、書き方も妥当です」という答えでもよい。

ただ、明らかに行儀の悪い書き方ってのもあって、これは直した方がいいなぁと思う指摘事項もまぁある。

とはいえ、修正してもコンパイル結果は全く一緒になるだろうなぁという事項がほとんどに思えた。

が、指摘事項の中で1つ気になることを発見してしまった。


それがこんなコードである。

if( (0 != foo & bar) && (val < VAL_MIN_FOO ) ){
  val = VAL_MIN_FOO;
}

ある条件にあてはまったときはvalの値に下限を設けて、下限より小さければ下限値に修正するというコードである。

これに対して「bool型とshort型の&演算をしています」という指摘があったのだ。

この書き方だと 0!=foo & bar は (0!=foo) & bar と解釈されてしまうんだよね。

この解釈は明らかに間違えていて、正しくはこうである。

if( (0 != (foo & bar) ) && (val < VAL_MIN_FOO ) ){

変数fooをbarでマスクして、それでも立っているビットがあれば、補正対象にするというのが正しい解釈である。


ただ、こんなに違うと当然意図したとおりに動かないわけで、なんでここまで気づかなかったのという話である。

このソースコードを書いたのはそれなりに前の話で、すでにこの部分はすでに動作確認済みという理解だったから。

それで、この部分の動作が間違っていたことによる影響を机上検討してみたところ、

なんとこの部分のソースコードが動いても動かなくても動作にはほとんど差がないということがわかった。

そもそもの設計がダメだったのだ。

というわけでこの部分について最初から見直した結果、今回指摘された部分のコードは消えることになった。


まさかこんな問題がこのタイミングで浮き出てくるとは思わなかっただけに、関係者は驚いたわけだが、

動作を見てるだけでは気づけない問題に気づけるのが解析ツールを使うメリットなのだろう。

解析ツールの指摘内容の大半はすでに考慮済み か 記述法はまずいが意図したとおりに動くものだから、

その点では解析ツールの結果はつまらないものが多いわけだが、よくよく調べてみるとバグが潜んでいるのを見つけられることがあると。

発見してしまったがために、それで1日潰れてしまったのは痛手だったが、長い目でみればよかったのかな。と思っている。


Author : hidemaro
Date : 2017/01/25(Wed) 19:29
C・C++ | Comment | trackback (0)

ifを入れ子にしているけどやりたいことはこういうこと

今日から新年の業務開始ということで、社長から新年の挨拶があって始まった。

今日はとあるプログラムをいじっていたのだが、既存のソースコードを読んで「うーん」と思ったものの、

結局それを踏襲してプログラムを書くはめになったという話。


どんなプログラムだったのかというとこんなの。

BOOL ret,result;

ret=A();
if(ret==TRUE){
  ret=B();
  if(ret==TRUE){
    ret=C();
    if(ret==TRUE){
      result = TRUE;
    }else{
      result = FALSE;
    }
  }else{
    result = FALSE;
  }
}else{
  result = FALSE;
}

なんかやたらifで入れ子になってるけど、

ようはA(); B(); C(); と返り値がTRUEであることを確認しながら順番に実行していくということだ。

しかし見た目がすさまじい。


そんなもんPerlで書けば、

A() or die;
B() or die;
C() or die;

で終わりだろという話になりそうだし、C/C++にしてもFALSEならreturnで関数から抜け出すとか書けばいいのでは? というツッコミがありそうだが、

一応、この後にも処理がありますので。無責任に抜け出して終わりとしては都合が悪いのだ。

もし、平気でgoto文を使うのならば、このように書いてもいいのかも知れないが。

BOOL ret,result;
ret=A();
if(ret==FALSE){
  result = FALSE;
  goto exit;
}
ret=B();
if(ret==FALSE){
  result = FALSE;
  goto exit;
}
ret=C();
if(ret==TRUE){
  result = TRUE;
}else{
  result = FALSE;
}
exit:

マシンコードにすれば、これもifが入れ子になったソースコードも同じようなものが出来上がると思うんだけどね。


一見したとき、なんでそんな入れ子にしてるんだと思うけど、

よく読み解いてみるとさきほど書いたようなgoto文をつかった動作が思い浮かぶはずだ。

じゃあgoto文をつかって書くかと言われると困ってしまうよね。

そうして今までの例によって書くことを選んだわけある。


今回のプログラムの場合、直接的には関係ないが、いくつかのプログラミング言語には例外という機能がある。

処理に失敗したら例外を投げて処理を終了し、上位のtry-catch構文で拾って相応の処理を行うことができると。

もしA(),B(),C()の3つの関数が処理に失敗したら例外を投げるとすれば、こういう書き方でもいいことになる。

bool result=true;
try{
  A();
  B();
  C();
}catch(Exception e){
  result=false;
}

ただ、例外が投げられるとリソースを食うから、例外の起きる頻度によっては例外を起こさない方法を考えた方がいいという話もあって、

例えば、.NET Frameworkの System.Int32.Parse のような文字列を数値などに変換するメソッドを使うことを考える。

このメソッドは変換に失敗したとき例外が発生するので、その例外を拾えば数値化できない文字列であったことはわかる。

けど、System.Int32.TryParseメソッドを使えば例外によらずに数値化できない文字列であることを把握できる。

どちらの方がいいかは場合によりけりだけど、失敗する頻度が高いのならTryParseを使う方がパフォーマンスが上がるでしょうとのこと。

そういう選択肢を選べるのなら考える価値はあるってことだ。


Author : hidemaro
Date : 2016/01/05(Tue) 22:44
C・C++ | Comment | trackback (0)

多次元配列を動的に確保してみる

以前、プログラムでハードウエアでやりたい画像処理のシミュレーションを行ってたら、あまりに大きな配列を作りすぎて実行時にエラーが。

あまりに大きな配列は静的に確保できないことがあるんですよね。

じゃあ動的に確保したいわけだけど。これは4次元配列だ。さてどうしようか。


ところでCの多次元配列は配列の配列というわけではない。

全要素数分の連続した領域を確保して使っている。

int a[10][20]
a[x][y];
int b[10*20];
b[x*10+y];

この上下に書いてあることはほぼ同じことをしていると言える。

だから、実はここのaの型というのは int** ではなくて、int(*)[20] とかいう型になる。


この仕組みを使えば多次元配列の確保が一発で出来る。

ただ、そのためには多次元配列を表すためにポインタ変数が必要になる。

int (*p)[20];
p=new int[10][20];

これはC++での表記ですね。純粋なCではcalloc使ってこんな風にすればいいのかな。

p=calloc(10*20*sizeof(int));

3次元以上でも同じようにすればいい。

あまりこんなポインタ変数を使うことはないけど、こうして使える。

そうそう、動的に確保したらdeleteしないといけませんよ。


そういえば関数ポインタの宣言も似てますよね。

int (*f)(int,int);

Cのポインタ型は複雑である。

まぁC++では関数ポインタというよりも関数オブジェクトだからあまり使わんけどね。


C++的にはこの方法よりもboost::multi_array使う方がよかったのかな。

boost::multi_array (Let's Boost)

まぁBoostない環境だったからなぁ。そんな環境でさくっと使えるとなればこの方法になるのかなと。


Author : hidemaro
Date : 2011/08/03(Wed) 23:50
C・C++ | Comment | trackback (0)

C++でaccumelate!

画像工学の夏休みの宿題で顔画像認識か図形認識の課題をやれというわけで、

ちょうど授業の発表を担当したことの実践ということもあって図形認識をやってみることにした。

その中で境界線追跡(図形の境界線を追跡して境界線を得る処理)の結果から周囲長を得るという処理を書く必要があった。

実はその部分は後の変更で使わなくなったのだけど、スマートに書ける方法がC++にもあるということで記事にしておこうとおもう。


ところでこのプログラムはC++で書いてるんですけど、図形を扱うに当たってBoostのGILモジュールを使ってる。

画像データの読み書きとデータ構造だけでアルゴリズムとかそういうのはないんだけどそれは好都合。

処理自体はイテレータがりがり動かしているだけで、いかにも画像処理という感じ。

日本語でチュートリアルがあるので参考までに

Generic Image Library Tutorial 日本語訳


それで境界線追跡の結果はチェイン符号化して表している。

チェイン符号化は右の画素へ進むことを1、そこから反時計回りに2,3,と番号を振っていって、右下の画素へ進むことを8と表して、これを連ねて線図形を表す。

そして周囲長はその図形の周囲の長さだけど、上下左右に1画素と斜めに1画素では長さが違う。

そこで上下左右に進んだ時は1、斜めに進んだ時は√2でカウントする必要がある。

チェイン符号化でいうと上下左右に進むことは1,3,5,7で、斜めに進むことは2,4,6,8だから、それに応じてカウントの仕方を変えればいいわけだ。


境界線追跡の結果はstd::vector<char>に入れて表したとすると、こんな風に処理を書くことが出来る。

double length=std::accumelate(chain.begin(),chain.end(),0.0,[](double s,char c){return s+(c%2==1?1.0:1.414)});

なんと1行で書けてしまった。

ここで0.0と書いてあるけど、初期値はdouble型ということにしておかないとうまくいかないので0ではいけない。

まぁけどラムダ式使ってるからC++0xじゃないとだめだけど。

元々ライブラリの型名が長すぎるから型推論のauto使うためにC++0x対応のVisual Studio 2010を使ってたのでちょうどよかった。


このstd::accumelateはRubyでいうEnumerable#injectに相当するような物。

C++にもこんなのあるんですね。ただ、使う前にこう書いておかないといけない。

#include <numeric>

そう、algorithmヘッダじゃなくてなぜかnumericヘッダなんですね。

というのもこの関数は関数オブジェクトを与えなければこの関数はただ要素の総和を取るだけの関数なんですね。

まぁ総和というわり+演算子で計算するだけだから、std::stringのリストを与えたら文字列を連結したりするからその時点でちょっと不思議だが。

ただ、それだけじゃなくて関数オブジェクトを与えればかなりいろいろなことができるわけでどこがnumericやねんともっぱらの評判だ。


こういう総和を取るとかそういう操作は多いので有用な関数ではないかなと思うところ。

まぁけどラムダ式なかったら使う気にはならんか。


Author : hidemaro
Date : 2011/07/23(Sat) 23:43
C・C++ | Comment | trackback (0)

Cの制御構造もPerlに見習おう

今日も組み込みの講座へ。

今日からマイコンのプログラミングというわけで、ディジタル入出力について一通り。


プログラムを自分でいじいじして遊んでる人もいたが、まずはサンプルのプログラムを写すところからはじまる。

ただ、そのプログラムに紛らわしいものもあった。

void wait(int time){
  int i,j;
  for(i=0;i<time;i++)
    for(j=0;j<250;j++);
}

こんなコードだが、写す時にセミコロンを正しく付けないと全くうまくいかない。

つまづいているひと多数。まぁあまりにしょうもないミスなのでちょっと言えばわかってくれるけど。


この2重forをわかりやすく書けば、

fot(i=0;i<time;i++){
  for(j=0;j<250;j++){}
}

となるかな。

1つ目のforは{ }内が1文だけだから{ }を省略している。

2つ目のforは{ }内がなにもしないという文なので、;だけ書いていると。

しかし、これを正しく理解している人はどれぐらいるのだろうかという疑問がある。


ここでPerlは明快だなと思うわけである。

Perlではif,for,whileなどの{ }は省略できない。

まぁPerlには後置ifとかあるけど、そういうのは使わないとすればそうなる。

それでCのelse ifに相当するものはelsifという特別のキーワードを持っている。

Cでの else if( ){ } というのは、else{ if( ){ } } と解釈されてるからいいんだけど、Perlではこれができんから。


もちろん{ }を省略できるおかげでうれしいこともある。

if(i<0)       return -1;
else if(i==0) return  0;
else          return +1;

こういうことはよくやるよね。1行でまとめて書けばコンパクトで読みやすくていい。

ただ、あくまでもifの対象になるのは後の1文だけ。2文以上書くのにはやっぱり{ }省略は出来ない。

だから意外と限られた状況でしか使えなくて、そう考えると、さっきのような例でも { } を書く方が無難なのかも知れない。


初心者向けの講座なのでこういう部分でも丁寧に書いておいた方が正しい理解が得られるんじゃないかなと思った。

特に今回のようななにもしないループを for(…); って書くのは正しいけどどうかとも思うのよね。

普通はこんなループ作らんから、一体なにをしてるのかなと思ってしまう。

それなら { } と書いておけば、なにもすることがないんだなと明快だ。

ぜひ人に説明する時は制御構造はPerlで書いているつもりで { } を欠かさないように心がけていこうとおもった。


ところでCで書くようなforってPerlとかRubyではあんまり書かんよな。

for my $i(1..$time){
  for my $j(1..250){
  }
}

と数値についてやるときは範囲演算子を使うのが普通。

配列についてやるなら、Perl・RubyだけでなくC#でもforeachを使えば出来る。

ただ、Cではforeachのような機能はない。だからこうやってfor(i=0;i<time;i++) とか書くわけだけど、

ここでiとjが出てくると1つ目のforでjが出てきたり、2つ目のforでiが出てきたりというミスをしやすい。

意図してやっているならそれでもいいんだけど、こんな風に数字を数えるのならそれではいけない。

どうにかならんもんかねとは思うけどCではどうにもならん話。C++ではどうなんだろ。

boost::counting_iterator とかかなぁ。けどalgorithmと組み合わせて使うぐらいしか使い道ないよな。

今のところは注意して書くしかないってことか。


Author : hidemaro
Date : 2011/06/25(Sat) 23:50
C・C++ | Comment | trackback (0)

bit単位でカリカリ操作

余った回数券の無駄遣いをしてから、今日も組み込みの講座にお出かけ。

今日もCの回。


ところでマイコンでは1bit単位での取扱が多い。

まぁ当たり前と言えば当たり前の話で、1つの端子では1bit分の信号しか出せない。

例えばAVRマイコンでポートBの0~3を出力、4~7を入力に使おうというなら、

DDRB=0x0f; //pin7~4:入力 pin3~0:出力
for(i=0;i<4;i++){
  PORTB=0x0f&~(1<<i);  //pin_i=off
  if     (!PINB&0x80)  return i*4+3;  //pin7==off
  else if(!PINB&0x40)  return i*4+2;  //pin6==off
  else if(!PINB&0x20)  return i*4+1;
  else if(!PINB&0x10)  return i*4;
}

とか書くのだろうかな。自分で書いたことはないけど、こんな感じだと。

1bit単位で入力と出力を決めたり、1ビット単位で出力したりしなければならない。

しかし1bit単位で操作しようにも、そのためにはand,or,notでガリガリやる必要があるのも事実。

めんどくさいね。


ところで研究室でH8で遊んでいたことはある。H8の開発環境にはルネサスのHEWを使ってたのだが、

自動で生成されるI/Oレジスタの定義ファイルがなかなか便利だった。

IO.PCR5=0x0f;
for(i=0;i<4;i++){
  IO.PDR5.BIT.B0=(i!=0);
  IO.PDR5.BIT.B1=(i!=1);
  IO.PDR5.BIT.B2=(i!=2);
  IO.PDR5.BIT.B3=(i!=3);
  if     (IO.PDR5.BIT.B7)  return i*4+3;
  else if(IO.PDR5.BIT.B6)  return i*4+2;
  else if(IO.PDR5.BIT.B5)  return i*4+1;
  else if(IO.PDR5.BIT.B4)  return i*4;
}

まぁ正しいかはともかくとして、このようにビット単位での操作ができる。

これの種明かしが今日の話の中であった。


どうもビットフィールドという仕掛けによるものらしい。

構造体の宣言をこのようにする。

struct bits{
  unsigned char B0:1;
  unsigned char B1:1;
  unsigned char B2:1;
  unsigned char B3:1;
  unsigned char B4:1;
  unsigned char B5:1;
  unsigned char B6:1;
  unsigned char B7:1;
};

ただB0~B7のunsigned charを並べた構造体を宣言しているように見えるが、:1というのがポイントで、これで1bit幅と示している。

1bit幅のB0~B7を並べた構造体を作ると言うことだ。

具体的な並び方は処理系依存だが、今回使っていた環境では1バイトの中でLSBからMSBにB0,B1,…,B7と1bitずつ当てはめられた。


さらに共用体と組み合わせて使うと便利だ。

共用体は同じメモリ領域を複数の型の変数で共有するというもの。

union byte{
  unsigned char BYTE;
  struct bits BIT;
}

こうすると同じメモリ領域についてunsigned charとして見た時の姿と、さっき作ったstruct bitsで見た時の姿で取り扱うことができる。

union byte z;
z.BYTE=0; z.BIT.B7=1; z.BIT.B1=1; z.BIT.B0=1;
printf("0x%x\n",z.BYTE);

こうすると、まずzの全bitを0にして、7bit,1bit,0bit目を1にして、結果として0x83となる。


便利なのは確かなのだが、最大の問題点は処理系依存であることだろう。

正直PCで使うのは避けた方がよさそう。どうなるかわかったもんじゃない。

あと、どうもコンパイラがあまり効率の良くないコードを出力する可能性があるらしい。

実際のところどんなもんなのか知らないのだけど、一般にはビット演算でカリカリ書く方がよいようだ。

そんなわけで自分から積極的に使う機能ではなさそうだ。HEWでは使っとくけど。


マイコンでビット演算をよく使うことを考えると、Cの回ではそのあたりよく練習しておくことが大切そう。

実際ビット演算の問題は練習問題の多くを占めていたのだけど、ちょっとややこしい問題が多かった印象。

何ビット目かを1にしよう、0にしようということが基本なので、そのあたりを簡潔に練習すればよかったのかなと。

まぁここでは実感沸かないのも事実かなとは思うので、マイコンのプログラミングで使う時、振り返りながらやればうまく理解できそう。


Author : hidemaro
Date : 2011/06/04(Sat) 23:48
C・C++ | Comment | trackback (0)

初心者がうまくCを学ぶ方法は

今日は組み込みの講座でC言語の1回目、組み込みプログラムで使う前にPCでいろいろやってた。

この会の参加者のバックグラウンドはいろいろで、基礎的な事項からやる必要がある。

そんなわけだからこの回ではCでのプログラミングを1からやる必要がある。


この回の講師の人はMIT OpenCourseWareの内容を元にして説明していた。

これは、アメリカのマサチューセッツ工科大学の講義の内容を公開しているものでこれのC言語の集中講義が元になっているよう。

と、まぁそんなことからもわかるとおり大学の授業で体系的に説明することを意図した内容になっている。

よくできているのはよくできているのだが、なかなかうまくいってなかったかなと思った。


Cのプログラムを作って動かしても何も表示されないのでは困る。

そんなわけでHello worldを作るにあたって、putsだとかprintfだとか標準入出力関数を使う。

しかしよく考えてみると標準入出力関数を使うというのを体系的に説明しようとすると、

関数を説明して、ヘッダファイルを説明して、そこで初めて使えるぐらいで、この講義でも確かにそれらの説明が終わった後で示されていた。

けど、それではHello worldすらかけないという問題がある。

この講義では一番最初にこう書けばHello worldと表示できるのだと言っているけど、それじゃあHello world以外はできない。

とまぁ、話を進めていく中で手を動かせないまま話を進めて行かざるえないような問題もありそう。

演習問題をやろうにもprintfを取り上げていないから、計算結果を表示できないなど都合が悪い。あとそもそも演習問題が難しい。


実際に物事をやりながらCでのプログラミングを学んで行くならば、

最初にputs・printfぐらいは先取りして、その上で進める必要がありそう。そういう意味ではHello worldって良く出来たものだよね。

その上でmain関数の意義はおいておいて、main関数だけ使って変数・演算子・制御構造について学んでいくというのがよさそう。

ここらへんは特に重要なので自分で考えて作れるようにすることが大切だと思うのだが、

そこまでの演習はできてなかったかなと。果たしてどれほどforの使い方が本当にわかっていたのだろうか。

その上で配列・ポインタ・関数・ヘッダについて学んで、そこで詳しく標準入出力をすればよいのではないかなと。


高専の授業では授業アンケートがあって、プログラミングの授業の区分が講義科目になっていたことに違和感を感じたものだが、

実際の授業の進め方は、半分講義、半分演習という形で、かなり演習色の強い科目だった。

まぁ数学の授業にも似てるかも知れませんね。あれも理屈はあるけど実際に具体的な数字や式で試してなんぼという面はある。

その数学も講義科目だったから、同じようなもんか。

もちろんしっかり説明を聞いて、どのように動くか、なぜこうすればよいかと学ぶことも大切ですが、

結局それを実際の場面で使えんことにはどうにもならんので、講義と演習は二人三脚でやっていかなあかんと。

どうも、それがうまくいっていないかなという気がした。

まぁ最終的にマイコンのプログラミングで不便がなければよいのですが、まぁ次回もあるので、うまくフォローしていきたいなと。


Author : hidemaro
Date : 2011/05/28(Sat) 23:55
C・C++ | Comment | trackback (0)

moveしたりforwardしたりするC++0x

今日は野良仕事をしに行っていたわけだが、まぁこのことは後に書くとしよう。

正味の仕事は3時間ほどだったので大したことはなかったが、これでもかなりの効果はあったようで。

手伝いに行った意味もあったものだ。


C++0xやるかと思って、改めてVisual Studio 2010のBeta版を入れてみた。今のところ一番いけてるコンパイラだと思う。(参考記事 : C++0xの化け物じみた右辺値参照)

これを見ると結構いろいろな機能に対応しているね。なかなかよい。

その上でいろいろ試していたのだが、おもしろいこともあるもんだ。


std::unique_ptrという右辺値参照を活用したスマートポインタがあるのだが、これってコンテナに入れられたかなと思って実験してみた。

std::vector<std::unique_ptr<std::string>> v(1);
std::unique_ptr<std::string> p1(new std::string("hage"));
v.at(0)=std::move(p1);
v.push_back(std::unique_ptr<std::string>(new std::string("hoge")));

なんか平然と入れられるし。

std::move付け忘れるとどうなるかって話ですが、へんなエラーが出てコンパイルに失敗する。

まぁしかしこれがいけるならshared_ptrよりもシンプルでいいよね。unique_ptr万歳!

これを回すときですが、せっかくなのでfor_eachしてみた。

std::for_each(v.begin(),v.end(),[](std::unique_ptr<std::string>& p){
  std::cout << *p << std::endl;
});

shared_ptrと違ってコピーできないので参照渡ししないとならん。constは必要に応じてどうぞ。

果たしてこう言うのが便利なのかと言われると難しいが、他のところにコピーして渡す気さえなければいいんじゃないの。


さて、もう左辺値を破壊してしまってもかまわないときにはstd::moveを使うのですが、std::forwardというのもある。

これはテンプレートに関係がある。

template <typename T>
Test TestCreator(T&& other){
  return Test(other);
}

こんなテンプレート関数があったとき、Tの型名は自動で推論される。

もしTest型の右辺値を渡せばT=Testとなるのは容易に予想できる。

ところがTest型の左辺値を渡すとT=Test&となってこのテンプレートが使われる。

なんでやねんという話だがT& &やT& &&やT&& &はT&になるというルールがあるらしく、Test& &&はTest&と同じ意味になってしまうから。

まぁなんにせよ、こう書いておけば右辺値も左辺値も歓迎と言うことになる。

ここで元が右辺値ならstd::moveしたいが、元が右辺値であるかチェックしたいと。

そこでstd::forwardで、これを使うことで判別して適当にしてくれると。

  return Test(std::forward<T>(other));

こんな風に書けば、自動で判定して適当な処理をしてくれる。

右辺値か左辺値かまで完全に転送するからPerfect Forwardingなんだと。

rvalue reference 完全解説 (本の虫)

これを見て初めて理解できた。


それにしてもこの機能はすばらしいね。C++0xすばらしい。

しかしそんな風に言ってますが、C++0xは2000年代にリリースできなかったんですよね。

本来ならC++09みたいになるはずだったのに失敗してしまったと。

果たしてどういう方向にまとまるのか。できるだけ早くまとめてほしいところ。

以下に素晴らしくてもコンパイラが対応してないと使えないもん。


Author : hidemaro
Date : 2010/04/01(Thu) 23:58
C・C++ | Comment | trackback (0)

丸いQtウイジェット

Qtで作るプログラムもほぼ完成まで来たなと思っている。
なんと丸3日でそれなりのものができたというのは驚くところ。
ただ、まだ詰めが甘いかなと思うところもあるし、他にもやることはあるし。
2日後の展示のためのプログラムなのですが、果たしていけるのだろうか。
今日のお題はQtで丸いウイジェットを作る方法。
普通はウイジェットっていうのは四角いものです。
ですが、丸の部分以外を表示しなければ丸く見えるということで丸いウイジェットを作れるわけです。
この辺は.NET Frameworkでもありますよね。
System.Windows.Forms.ControlのRegionプロパティにしかるべき領域を設定すると。
それに相当するQtのはQWidgetのsetMaskですね。
Circleクラスとして作ってみた。
まずヘッダファイル。
#ifndef CIRCLE_H
#define CIRCLE_H
#include <QtGui/QWidget>
class Circle : public QWidget{
Q_OBJECT
public:
Circle(QWidget* parent=0);
};
#endif // CIRCLE_H
単に継承しただけです。なんてことはありません。
そしてソースファイル。
#include "circle.h"
Circle::Circle(QWidget *parent) : QWidget(parent){
this->setMaximumSize(60,60);
this->setMinimumSize(60,60);
this->setMask(QRegion(QRect(0,0,60,60),QRegion::Ellipse));
QPalette mypalette;
mypalette.setColor(QPalette::Window,QColor(Qt::red));
this->setPalette(mypalette);
this->setAutoFillBackground(true);
}
setMaskにQRegionのオブジェクトを与えて設定する。
あと、色を変えるためにパレットをいじったりしているが、基本的にはsetMaskするだけ。
さて、この自作ウイジェットをフォームに貼り付けよう。
と考えるのはもっともな話でどうやって貼ろうか僕も考えたものです。
デザイナーから自作ウイジェットを貼る方法は少々複雑です。
その自作ウイジェットの基底クラスのウイジェットを配置して、それを格上げするという方法でやります。
その配置したウイジェットで右クリックして、「格上げ先を指定」で設定するわけです。
新しい格上げされたクラスでCircleクラスを追加して、その追加されたのを選らんで格上げとすればいいわけです。
見た目は何も変わりませんが、これでCircleクラスのウイジェットに化けたわけです。
これで実行してみると赤い丸が見えるはず。
こんな丸いもん作ってどうすると思う人もあるかも知れない。
けど画像として丸を表示するよりも良い点もあるわけです。
例えば丸の部分だけがクリックに反応したり。
なかなか面白いのでいろいろ試してみてください。
Author : hidemaro
Date : 2009/10/28(Wed) 23:41
C・C++ | Comment | trackback (0)

Tools