ログイン

2016年3月 弊社ホームページは新しくなりました。 https://thinkridge.com

メインメニュー

携帯公式サイト


携帯電話をもっと便利に
もっと楽しく


史上初の吹奏楽専門着メロサイト


POPで癒しでライトでとんがって気持ちのいい〜オルゴール着メロをあなたに

Magome

クラウドベースの MIDI シーケンサ Magome

音楽制作に興味のある方を対象に、スタンドアロンでも使え、ネットならではの面白さも兼ね備えた音楽制作アプリの提供を目指しています

for 携帯電話

https://thinkridge.com/m/
ケータイはこちらへ

2015年2月1日(日曜日)

boost::asio で UDPホールパンチング

カテゴリー: - takatsuka @ 22時21分43秒

NAT 越えの技術は skype が出てきたころには話題になってる気がしますが、自前のソフトに組み込んで使えるような標準的な形に既になってるのかよくわからないってこともあり、興味本位で試してみました。

UDP ホールパンチングによる NAT 越えです。
で、boost::asio の udp の勉強も兼ねてサンプルアプリを作ってみました。

とはいえ、boost::asio の udp については難しいことは何もなく tcp よりシンプルに使えました。有難いことです。開発者の方に感謝です。

さて、UDP ホールパンチングについて。
wikipedia みると NAT の実装にはいろいろあるみたいですが、大ざっぱな分類でいう Port-Restricted cone NAT という環境同士までの P2P 通信は実現出来ました。
Full cone NAT とか Address-Restricted cone NAT の環境は試せてないんですが、Port-Restricted より条件緩いものなので、これらもたぶん問題なさそう。

で、Symmetric NAT って言われる環境はやっぱり無理そうでした。

docomo の LTE回線、純正のSPモード と MVNO (nifty の一番安いヤツ) を試しましたが、これに該当してそうです。
スマホのテザリングで繋げた PC で試したので、Symmetric NAT がプロバイダ側にあるのか、スマホのテザリング機能なのか判断できませんが残念。早々に諦めました。

で、以下は自分自身向けの備忘録もかねて、コードの抜粋と、軽く説明を・・。

  • ヘッダ
namespace RLib
{

    class CUdpSocket         :private boost::noncopyable     {         class CSocket;         const boost::shared_ptr<CSocket>    m_spSocket;     public:         CUdpSocket(asio::io_service &ioService);         ~CUdpSocket();         void Close();         bool Bind(unsigned short nPort,boost::system::error_code &ec=boost::system::error_code());         bool Connect(const std::string &sDomain,const std::string &sPort);         bool SendTo(const boost::asio::ip::udp::endpoint &endPoint,                     const boost::shared_ptr<const vector<char>> &spData,                     boost::system::error_code &ec=boost::system::error_code());         bool Send(const boost::shared_ptr<const vector<char>> &spData,                   boost::system::error_code &ec=boost::system::error_code());

        // データ受信         // ・次の読み込み待ちにする場合には Receive(); をコールすべし。コールしない場合には何もしない。         typedef boost::function<void (CUdpSocket &udpSocket,const boost::system::error_code &ec,                                 const boost::asio::ip::udp::endpoint &endPointRemote,                                 const boost::shared_ptr<const vector<char>> &spReceivedData)> FuncOnReceived;         bool Receive(const FuncOnReceived &funcOnReceived,unsigned short nBufferSize=8192);

    public:         static CString GetTextAddress(const asio::ip::udp::socket::endpoint_type &ep)             {                 return CRString::Format(_T("%s:%d"),CString(ep.address().to_string().c_str()),ep.port());             }     };

}

class CUdpHolepunching {     class CMain;     CMain   &m_main; public:     typedef boost::function<void (const CString &s)> FuncMessage;     CUdpHolepunching(const FuncMessage &funcMessage);     ~CUdpHolepunching();

    void Server(unsigned short nPort);     void Client(const std::string &sDomain,const std::string &sPort);

};

  • .cpp
class CUdpSocket::CSocket
{
public:
    CUdpSocket      *m_pUdpSocket;
    ip::udp::socket m_socket;
public:
    CSocket(CUdpSocket &udpSocket,io_service &ioService)
        :m_pUdpSocket(&udpSocket)
        ,m_socket(ioService)
        {
            m_socket.open(ip::udp::v4());
        }
};

CUdpSocket::CUdpSocket(asio::io_service &ioService) :m_spSocket(new CSocket(*this,ioService)) { }

CUdpSocket::~CUdpSocket() {     m_spSocket->m_pUdpSocket = NULL;        // 破棄されたマーク }

void CUdpSocket::Close() {     m_spSocket->m_socket.close(); }

bool CUdpSocket::Bind(unsigned short nPort,boost::system::error_code &ec) {     return m_spSocket->m_socket.bind( ip::udp::endpoint(ip::udp::v4(),nPort), ec ) == false;    // エラーなら }

bool CUdpSocket::Receive(const FuncOnReceived &funcOnReceived,unsigned short nBufferSize) {     struct F{         static void OnReceived(const boost::system::error_code &ec,size_t bytesReceived,                                boost::shared_ptr<CSocket> spSocket,                                boost::shared_ptr<ip::udp::endpoint> spEndpointRemote,                                boost::shared_ptr<vector<char>> spBuffer,const FuncOnReceived funcOnReceived)         {             if( !spSocket->m_pUdpSocket ) return;                       // 破棄されてる?             if( ec ){   // Error                 if( ec == boost::asio::error::operation_aborted ) return;                 //return; エラーでもコールはする             }

            spBuffer->resize(bytesReceived);             if( funcOnReceived ){                 funcOnReceived( *spSocket->m_pUdpSocket, ec, *spEndpointRemote, spBuffer );             }         }     };

    boost::shared_ptr<ip::udp::endpoint> spEndpoint(new ip::udp::endpoint);     boost::shared_ptr<vector<char>> spBuffer(new vector<char>(nBufferSize));    // 受信バッファ     m_spSocket->m_socket.async_receive_from(         asio::buffer(*spBuffer),         *spEndpoint,         boost::bind(             &F::OnReceived,             boost::asio::placeholders::error,             boost::asio::placeholders::bytes_transferred,             m_spSocket,spEndpoint,spBuffer,funcOnReceived)     );

    return true; }

bool CUdpSocket::SendTo(const boost::asio::ip::udp::endpoint &endPoint,                         const boost::shared_ptr<const vector<char>> &spData,boost::system::error_code &ec) {     if( !spData ){         BOOST_ASSERT(false);         return false;     }     const size_t size = m_spSocket->m_socket.send_to( asio::buffer(*spData), endPoint, 0, ec );     if( !ec ) return size == spData->size();     ATLTRACE( _T("nCUdpSocket Error Send -> %s"), CString(ec.message().c_str()) );     return false; }

bool CUdpSocket::Send(const boost::shared_ptr<const vector<char>> &spData,boost::system::error_code &ec) {     if( !spData ){         BOOST_ASSERT(false);         return false;     }     const size_t size = m_spSocket->m_socket.send( asio::buffer(*spData), 0, ec );     if( !ec ) return size == spData->size();     ATLTRACE( _T("nCUdpSocket Error Send -> %s"), CString(ec.message().c_str()) );     return false; }

////////////////////////////////////////////////

class CUdpHolepunching::CMain     :public CWindowImpl<CMain> { public:     DECLARE_WND_CLASS( _T("CMain") );     BEGIN_MSG_MAP(CMain)         MESSAGE_HANDLER(WM_TIMER, OnTimer)     END_MSG_MAP() public:     LRESULT OnTimer(UINT, WPARAM, LPARAM, BOOL&)         {             m_ioService.poll();             return 0;         } public:     const CRUid                 m_id;     FuncMessage                 m_funcMessage;     io_service                  m_ioService;     CUdpSocket                  m_udpSocket;     auto_ptr<deadline_timer>    m_apTimer;

#pragma pack(push,1)     struct CEndpoint     {         unsigned long   m_nIpv4;         unsigned short  m_nPort;         CEndpoint()             {}         CEndpoint(const ip::udp::endpoint &endPoint)             {                 m_nIpv4 = endPoint.address().to_v4().to_ulong();                 m_nPort = endPoint.port();             }     }; #pragma pack(pop)

    std::map<CRUid,CEndpoint>   m_mapMember;

    void OnReceived(CUdpSocket &udpSocket,const boost::system::error_code &ec,                     const boost::asio::ip::udp::endpoint &endPointRemote,                     const boost::shared_ptr<const vector<char>> &spBuffer)         {             if( ec ){   // Error                 m_funcMessage( CRString::Format(_T("%s : OnRecived Error [%s] %s"),                                CString(m_id.GetText().c_str()), CUdpSocket::GetTextAddress(endPointRemote),                                CString(ec.message().c_str())) );             }else{                 m_funcMessage( CRString::Format(_T("%s : OnRecived [%s]"), CString(m_id.GetText().c_str()),                                CUdpSocket::GetTextAddress(endPointRemote) ) );

                // メンツリスト更新                 vector<char> vBuffer(*spBuffer);                 if( vBuffer.size() >= sizeof(CRUid) ){ // このソケットの送り主を自身のメンツリストにマージ                     CRUid id;                     std::copy( vBuffer.begin(), vBuffer.begin()+sizeof(id),                                stdext::checked_array_iterator<char*>(reinterpret_cast<char*>(&id),sizeof(id)) );                     vBuffer.erase( vBuffer.begin(), vBuffer.begin()+sizeof(id) );                     m_mapMember[id] = CEndpoint(endPointRemote);                 }else BOOST_ASSERT(false);                 // 送り主からのメンツリストを自身のメンツリストにマージ                 while( vBuffer.size() >= sizeof(CRUid)+sizeof(CEndpoint) ){                     CRUid id;                     std::copy( vBuffer.begin(), vBuffer.begin()+sizeof(id),                                stdext::checked_array_iterator<char*>(reinterpret_cast<char*>(&id),sizeof(id)) );                     vBuffer.erase( vBuffer.begin(), vBuffer.begin()+sizeof(id) );                     CEndpoint ep;                     std::copy( vBuffer.begin(), vBuffer.begin()+sizeof(ep),                                stdext::checked_array_iterator<char*>(reinterpret_cast<char*>(&ep),sizeof(ep)) );                     vBuffer.erase( vBuffer.begin(), vBuffer.begin()+sizeof(ep) );                     if( id != m_id ){                                       // 自分自身は除く                         m_mapMember[id] = ep;                     }                 }

            }

            udpSocket.Receive( boost::bind(&CMain::OnReceived,this,_1,_2,_3,_4) );    // 次の読み込み待ちを開始         }

public:     CMain(const FuncMessage &funcMessage)         :m_id(CRUid::RandomGenerator())         ,m_funcMessage(funcMessage)         ,m_udpSocket(m_ioService)         ,m_apTimer(new deadline_timer(m_ioService))         {             Create(HWND_MESSAGE,CRect(0,0,0,0),_T(""),NULL);             ::SetTimer( m_hWnd, 0, 1, NULL );              m_apTimer->expires_from_now( boost::posix_time::millisec(0) );          // nTime秒後に             m_apTimer->async_wait( boost::bind( &CMain::OnDeadlineTimer, this, _1 ) );         }

    ~CMain()         {             m_apTimer.reset();             m_udpSocket.Close();             if( m_hWnd ) DestroyWindow();         }

    void OnDeadlineTimer(const boost::system::error_code& error)         {             if( error || error==boost::asio::error::operation_aborted ) return; // チェック

            if( m_apTimer.get() ){                      // 終了していないなら次のタイマーを設定                 m_apTimer->expires_at( m_apTimer->expires_at() + boost::posix_time::millisec(5000) );                 m_apTimer->async_wait( boost::bind( &CMain::OnDeadlineTimer, this, _1 ) );             }

            // メンツリスト送信             boost::shared_ptr<vector<char>> spData(new vector<char>);             std::copy( reinterpret_cast<const char*>(&m_id), reinterpret_cast<const char*>(&m_id)+sizeof(m_id),                        std::back_inserter(*spData) );             for(std::map<CRUid,CEndpoint>::const_iterator i=m_mapMember.begin(); i!=m_mapMember.end(); i++ ){                 const CRUid &id = i->first;                 std::copy( reinterpret_cast<const char*>(&id), reinterpret_cast<const char*>(&id)+sizeof(id),                            std::back_inserter(*spData) );                 const CEndpoint &ep = i->second;                 std::copy( reinterpret_cast<const char*>(&ep), reinterpret_cast<const char*>(&ep)+sizeof(ep),                            std::back_inserter(*spData) );             }

            CString sSendTo;             for(std::map<CRUid,CEndpoint>::const_iterator i=m_mapMember.begin(); i!=m_mapMember.end(); i++ ){                 const CEndpoint &ep = i->second;                 ip::address addr(boost::asio::ip::address_v4(ep.m_nIpv4));                 ip::udp::endpoint endpoint( addr, ep.m_nPort );                 m_udpSocket.SendTo( endpoint, spData );                 sSendTo.Format(_T("%s [%s]"),CString(sSendTo),CUdpSocket::GetTextAddress(endpoint));             }             if( !sSendTo.IsEmpty() ){                 m_funcMessage( CRString::Format(_T("%s : SendTo %s"), CString(m_id.GetText().c_str()), sSendTo ));             }         }

    void Server(unsigned short nPort)         {             boost::system::error_code ec;             if( !m_udpSocket.Bind(nPort,ec) ){                 m_funcMessage( CRString::Format(_T("%s : Bind Error [%s]"), CString(m_id.GetText().c_str()),                                                 CString(ec.message().c_str()) ) );                 return;             }             m_udpSocket.Receive( boost::bind(&CMain::OnReceived,this,_1,_2,_3,_4) ); // 読み込み待ちを開始         }

    void Client(const std::string &sDomain,const std::string &sPort)         {             boost::shared_ptr<vector<char>> spData(new vector<char>);             std::copy( reinterpret_cast<const char*>(&m_id),                        reinterpret_cast<const char*>(&m_id)+sizeof(m_id), std::back_inserter(*spData) );              ip::udp::resolver resolver(m_ioService);             ip::udp::resolver::query query(ip::udp::v4(), sDomain, sPort );             boost::system::error_code ec;             ip::udp::resolver::iterator iterator = resolver.resolve(query,ec);             if( ec ){                 m_funcMessage( CRString::Format(_T("%s : resolve Error [%s]"),                                               CString(m_id.GetText().c_str()), CString(ec.message().c_str()) ) );                 return;             }             m_udpSocket.SendTo( *iterator,spData );             m_udpSocket.Receive( boost::bind(&CMain::OnReceived,this,_1,_2,_3,_4) );   // 読み込み待ちを開始         }

};

CUdpHolepunching::CUdpHolepunching(const FuncMessage &funcMessage) :m_main(*new CMain(funcMessage)) { }

CUdpHolepunching::~CUdpHolepunching() {     delete &m_main; }

void CUdpHolepunching::Server(unsigned short nPort) {     m_main.m_ioService.post( boost::bind( &CMain::Server, &m_main, nPort ) ); }

void CUdpHolepunching::Client(const std::string &sDomain,const std::string &sPort) {     m_main.m_ioService.post( boost::bind( &CMain::Client, &m_main, sDomain, sPort ) ); }

UDP ホールパンチングの仕組み上、穴をあけずとも UDP が通る環境が一つ必要です。
STUN サーバーと言われるもの。
サンプルアプリでは「サーバー起動」ってすると、UDP ポートを指定して待ち受け状態になります。
(とはいえ、標準化されてるプロトコルを実装してるわけではなく、オレオレ実装です。)

各端末(クライアントPC)は、このサーバーに接続することで、自身の NAT に穴を開けることと同時に、サーバーから各端末のエンドポイント情報を貰います。
あとは、各端末同士がそのエンドポイントに対して送信し合うことで P2P 通信を行う。という寸法です。

サンプルアプリでは、サーバーもクライアントも、すべての相手に対し、キープアライブも兼ねて、5秒毎に各端末のエンドポイント情報を送信し続けます。

というわけで、NAT 越えの P2P 通信自体は出来ることがわかったんですが、実用的にするのはいろいろハードルがあって、
Symmetric NAT 内の端末に対しては、サーバーなりが橋渡しをして通信しないといけない。とか、
UDP すら通さないような環境向けに TCP で橋渡しするようなサーバーを用意する。とか、
UDP を使うので、TCP 並の信頼性とか順序の安定性が必要なら自前実装しなきゃ。とか、
同じ NAT 内の端末同士は、ローカルIPで通信させるなり対策しなきゃ。(たぶん一般的なルーターでは、自分自身が開けたポートに自分自身から接続は出来なさそうなので)。とか。

この程度は世の P2P アプリ(skypeとか)は当然のように実現してるんだと思います。御見それです。

これらも楽しそうなので作ってみたいですが、仕事かなにかで必要になってくれないと手を出しずらいです。大変そうなので。

上記のソース一式はこちらからお願いします。例によって windows 用です。MFC 使ってます。
なにか間違いとかご意見とかればぜひお願いします。

ありがとうございました。


2014年5月9日(金曜日)

template クラスの中の template の定義

カテゴリー: - takatsuka @ 23時36分03秒

C++ は使い続けてないと書式を忘れてしまいます。老化かな。
というわけで書き方を覚えていられず再び悩んでしまったので備忘録の為に。

template <typename TA> struct ClassA
{
    template <typename TB> struct ClassB
    {
        static const TB id;
        template <int TC> struct ClassC
        {
            static int Func();
        };
    };
};

template <typename TA> template<typename TB> const TB ClassA<TA>::ClassB<TB>::id = 999;

template <typename TA> template<typename TB> template<int TC> int ClassA<TA>::ClassB<TB>::ClassC<TC>::Func() {     return TC * TC; }

void Func() {     long i = ClassA<int>::ClassB<long>::id;     int n = ClassA<double>::ClassB<float>::ClassC<1>::Func(); }


2013年11月20日(水曜日)

boost::thread_specific_ptr

カテゴリー: - takatsuka @ 23時36分22秒
boost::thread_specific_ptr がとても便利なのでよく利用させてもらってます。ありがとうございます。
がしかし、boostのバージョンを最新にすると、実行時に reset で落ちるようになってしもた。
boost1.51はOK。1.52以降だと落ちるみたい。
単純なサンプルプログラム程度だと正常なんだけど、magome では落ちてしまう。
自分のスキルでは、boost の中のコードを追ったり、使い方の問題なのかとかの原因の切り分けが難しいので、とりあえず、boost1.51 を使い続けることにしようか・・とはいえ、今後新しい boost を使えないってのは、ちょっと残念だし・・・。
いっそのことまったく同じ機能のものをこさえちゃうか!?ビバ車輪の再発明!!
・・ウソデス。boost::thread_specific_ptr かその使い方を調べたほうがいいすね。スミマセン・・・。

2013年6月15日(土曜日)

boost::asio ソケットを別プロセスに渡す

カテゴリー: - takatsuka @ 10時11分29秒

久しぶりに boost::asio ネタです。

サーバーで、acceptしたソケットを、別のプロセスに渡したい場合があると思います。
そのあたり boost::asio では簡単にできるんだろうか?と思い、ソースを眺めたり、Examples 見たりしたけど、そのものズバリっぽいものが見当たらない。
Examples には fork の項に近いのがあったんですが、fork() とか使ってるとこからして windows 向けではなさげだし・・・、

というわけで、今回のネタも windows 向けです。
サーバーを windows でってのがそもそも・・・という意見もありそうですが、windows でも P2P アプリを作りたいとか、ちょっとは需要あると思うんです。

というわけで、boost::asio で、accept したソケットを別のプロセスに渡す方法ですが、
結局、boost::asio だけで閉じることは出来ず win32 api を使ってます。

やることは、
複製元のソケットで、

boost::asio::ip::tcp::socket socket;
・・・
WSAPROTOCOL_INFO pi = {0,};
::WSADuplicateSocket( socket.native(), ::GetCurrentProcessId(), &pi);
SOCKET h = ::WSASocket( pi.iAddressFamily, pi.iSocketType,pi.iProtocol, &pi, 0, 0 );
socket.close();

ってハンドルをこさえて、複製先の子プロセスでは、

socket.assign( boost::asio::ip::tcp::v4(), h );

とやることで実現してます。

注意点としては、

#define BOOST_ASIO_DISABLE_IOCP 1

を入れることなんですが、これを入れることのデメリットがよくわからずにやってます。

こさえたサンプルアプリのソース一式を置いておきますので、必要な方は見てみてください。そしてダメ出しとかお願いします。
クライアント側がないと動作が見にくいという場合、boost::asio 使ったチャットソフトが同じところにありますので、そちらも参照下さい。

http://www.thinkridge.com/modules/mydownloads/

そういえば、いつのまにか boostpro サイト が終了してたんですね。
インストーラーは便利だっただけにとても残念です。
運営の方、お疲れ様でござました。感謝です。


2013年5月12日(日曜日)

WebKit (windows版) のビルド

カテゴリー: - takatsuka @ 21時42分52秒

WebKit (windows版) のビルド

けっこう苦労したので備忘録として。

開発環境の準備
・VisualStudio 2005 standard をインストール。

・Microsoft Platform SDK for Windows Server 2003 R2 をインストール

・Microsoft DirectX SDK (February 2010) をインストール

・QuickTime SDK をインストール

・VisualStudio2005 のサービスパックとか WindowsUpdate で出てくる VisualStudio 系のパッチを、全てインストール

・Cygwin をインストール。( webkit.org にある cygwin-downloader.zip )

・VisualStudio2005 の メインメニュー [ツール] - [オプション] - VC++ ディレクトリ で、以下を追加
 実行可能ファイル
  C:/Program Files/Microsoft Platform SDK for Windows Server 2003 R2/Bin
 インクルードファイル
  C:/Program Files/Microsoft Platform SDK for Windows Server 2003 R2/Include
  C:/Program Files/Microsoft Platform SDK for Windows Server 2003 R2/Include/mfc
  C:/Program Files/Microsoft DirectX SDK (February 2010)/Include
  C:/Program Files/QuickTime SDK/CIncludes
 ライブラリファイル
  C:/Program Files/Microsoft Platform SDK for Windows Server 2003 R2/Lib
  C:/Program Files/Microsoft DirectX SDK (February 2010)/Lib/x86
  C:/Program Files/QuickTime SDK/Libraries

ソ−スの用意

・WebKit Open Source Project の WebKit Nightly Builds から、ソースを取得 ( 自分の場合は WebKit-r149799 )

・C:/cygwin/home/(user)/WebKit に展開。

・WebKitSupportLibrary.zip をダウンロードして、zipファイルのまま上記のフォルダに配置。

・cygwinを起動。

・$ WebKit/Tools/Scripts/update-webkit を実行。成功を確認する。

ビルド

・$ WebKit/Tools/Scripts/build-webkit –debug を実行。

・或いは、C:/cygwin/home/(user)/WebKit/Source/WebKit/win/WebKit.vcproj/WebKit.sln を vs2005 で開き、ビルド。

ビルドエラーを取る

・C:/cygwin/home/(user)/WebKit/Source/WTF/WTF.vcproj/copy-files.cmd の
# FIXME: Why is WTF copying over create_hash_table? の行をコメントアウト。

・warning C4819:ファイルは、現在のコード ページ・・・と出るファイルを全て、unicode 形式で保存しなおす。

実行

・Windows 用 Safari 5.1.7 をインストール。

・WebKit.sln を vs2005 で開き、WebKitLib をスタートアッププロジェクトにする。

・WebKitLib のプロパティページ - デバッグ で以下に設定
 コマンド $(PROGRAMFILES)/Safari/Safari.exe
 環境 WEBKITNIGHTLY=$(OutDir)

・WebKitLib を実行。
 ビルドしたDLLを利用する形で safari が起動する。
 がしかし、実行したとたんに、実行時エラーで終了。とりあえずここまでで週末終了。

注意事項

・WebKit Nightly Builds から古いものが取得出来るが、WebKitSupportLibrary.zip とのバージョン不整合が起こるのか、update-webkit が失敗する。解決策不明。

・DirectX SDK は、VisualStudio2005 で使える版を使う。

・プロジェクト : warning PRJ0018 : 以下の環境変数が見つかりませんでした: $(PRODUCTION) とか出ることがあるが無視してOKらしいので無駄に悩まないこと。

以上、webkit の関係者に感謝しつつ、どなたかの参考になれば幸いです。


2009年5月9日(土曜日)

STL auto_ptr のデバッグ豆知識

カテゴリー: - takatsuka @ 16時39分20秒

VisualC++2005,2008 限定です。

auto_ptr を使うと、生ポインタが隠蔽される為か、デバッガのウォッチ画面での確認が面倒になります。

auto_ptr

この例で言うと、ap をウォッチしてみても CSub という型までは知ることが出来ますが、CSub の中身を見ることが出来ません。

ここで諦めてしまうとこですが、auto_ptr のメンバ変数 _Myptr までを入力(この例で言うと「ap._Myptr」と)するとアラ不思議、CSub の中身まで見ることが出来ます。

というか、手動で入力せずとも普通に見れて欲しいとこです。次期 VisualStudio2010 で是非対応して欲しいです。

なお、仮想関数を持たない非ポリモーフィックなクラスでは面倒みてくれないようで表示されません。(これは納得できます。)
しかし、その場合でも手動でキャストすると表示されます。「(CSub*)ap._Myptr」とします。
型がわかっている場合にしか使えませんが、多少はラクになるかもです。

ちなみに boost::shared_ptr は特別なことしなくても、メンバ変数 px が何故か普通に見れます。

となると、何故 auto_ptr だけダメなのか、
難しい記述をしているのか?
マイクロソフトが VisualStudio2005、2008 で何かやらかしてしまったのか?
上位エディションだと実はちゃんと見れるとか!? (自分 Standard エディション使ってます。)
少し興味沸くところですが・・・ま、いいかなと。

以上。中途半端なネタですみません。


2008年5月17日(土曜日)

相互参照のクラスをヘッダファイルだけで実現

カテゴリー: - takatsuka @ 00時29分52秒

struct ClassB;

struct ClassA
{
    int n;
    int Func(ClassB &b) {return b.n;}
};

struct ClassB
{
    int n;
    int Func(ClassA &a) {return a.n;}
};

こんなような、お互いを参照するクラスがあったとします。

しかし、これではコンパイルが通りません。
コンパイラが ClassA::Func を処理するときには、ClassB が分からないってことでエラーとなります。

そこで通常なら、ヘッダファイルに定義を書いて、実装はソースファイルに書くことになると思います。

— class.h —

struct ClassB;

struct ClassA
{
    int n;
    int Func(ClassB &b);
};

struct ClassB
{
    int n;
    int Func(ClassA &a) {return a.n;}
};

— class.cpp —

#include “class.h”

int ClassA::Func(ClassB &b) {return b.n;}

しかし、どうしてもヘッダファイルで完結させたい場合があります。
ヘッダファイルをインクルードするだけで使えるクラスってのは、なにかと便利です。

そんな場合は、こうすると解決することを思いつきました。

struct ClassB;

struct ClassA
{
    int n;
    int Func(ClassB &b) {return FuncT<0>(b);}
private:
    template <int dummy> int FuncT(ClassB &b)   {return b.n;}
};

struct ClassB
{
    int n;
    int Func(ClassA &a) {return a.n;}
};

無理やり template 関数を使います。(template の引数は使いません。)
これでコンパイルが無事に通ります。

ただ、試した環境は VisualStudio2005 と 2008 だけなので、古い環境などではエラーになることもあるのかもしれません。
というか、最初のコードでも無事にコンパイルしてくれるコンパイラもあるかもしれないっすね。

ちなみに、

template <int dummy=0> struct ClassAT
{
    int n;
    int Func(ClassB &b) {return b.n;}
};

typedef ClassAT<> ClassA;

こんな感じでもOKですが、余計な公開クラスが出来てしまうのが欠点すね。

 

以上、最近思いついた姑息テクです。
もっと良い解決策をご存知の方おられましたら、是非ご教授頂き度。

/*
相互参照するクラス自体が好ましくないのかな・・・?
*/


2007年1月28日(日曜日)

STLとMFCのマップ速度比較

カテゴリー: - takatsuka @ 11時30分29秒

プログラマらしいネタを・・。

C++で使える標準ライブラリに STL というのがあり、さらに VisualC++ には MFC っていうクラスライブラリがあります。
この二つのライブラリには結構ダブる機能があるります。文字列、配列、マップなどなど。
なので、Windows限定でコーディングする場合、どっちを使えばよいか悩むときがあります。

というわけで、これらダブる機能のうち、パフォーマンスが気になってしまった マップ について速度比較してみました。
STL からは map と hash_map、MFCからは CMap の3種類です。

というわけで、C/C++の小手先最適化テクニック にコンテンツとして追加いたしましたので、興味がある方は是非ご覧ください。
もしかして自作のベンチマークアプリにミスとか偏りとかがあるんじゃないかと少々不安なので、もし何かお気づきになられましたら、ご意見などいただければ幸いです。


2006年6月23日(金曜日)

VisualStudio2005 の C++/CLI

カテゴリー: - takatsuka @ 00時42分50秒

VisualStudio2005 の C++/CLI はスゴイっす。
最近までは、無料版の VisualStudio2005 ExpressEdition を試していました。
C# や VB でコーディングする分には普通に使えますし ClickOnce も出来ます。
C++ でも Platform SDK をインストールすることで、ネイティブなWin32アプリケーションが書けます。がしかし自分の場合は、MFC で書いた過去のコード資産や慣れを活かしたくて、ExpressEdition だとちょっと物足りなく感じ、StandardEdition に移行しました。
一番興味深かったのが C++/CLI で、過去のソフトウェアをマネージドコードと連携できるのか?というとこだったので、過去アプリの中から、MFCのみならず libpng zlib というC言語のライブラリも使った画像変換アプリを題材にしてみたのですが、ものの見事に期待通りの動作を実現してしまいました。スゴイです C++/CLI。
C# と連携させることで .NET Framework2.0 の ClickOnce で配信まで出来ました。スゴイです。
で、早速アプリケーションを公開してみました。こちらからどうぞ。
下馬評では、C++/CLI は既存コードを生かす為だけに使って、新規でコーディングするなら C# や VB を使ったほうが良い。と言われているようですが、自分は C++/CLI はメインで十分使えそうな気がしました。使いやすいし何でも出来ますよ。単に C++ で慣れてるからそう思うだけかもしれませんが。
というわけで、VisualStudio2005 & C++/CLI のスゴさを実感できたので、今後は徐々にそれを活かしていこうと思いました。


44 queries. 0.059 sec.
Powered by WordPress Module based on WordPress ME & WordPress

カレンダー
2024年 4月
« 11月    
 123456
78910111213
14151617181920
21222324252627
282930  
月別過去ログ
カテゴリ一覧
検索
最近の投稿
最近のコメント
投稿者ブロック