2014年12月17日水曜日

入社二年目のプログラムをなおしてみた。

日本の最高学府の大学院を卒業後、とある会社に入社した二年目の社員が、なぜプログラムが落ちるのかわからなくて悩んでいる。しかももう一ヶ月も。

やっぱ、企業ってお金稼ぐことも大切ですが、雇用の安定とか社会的な責任を果たすことも大事ですからね。

わかりやすくするためにコードを簡単にするとこんな感じ。

#include <stdio.h>
void func(int* a){
*a=12345;
}
int main(int argc,char* argv[])
{
int a;
func((int*)a);
printf("a=%d\n",(int)a);
return 0;
}

うーん、これはとっても難しい問題だね。
なのでどうすれば動くのか一緒に考えてみました。


案その1:

グローバルなint*をオペレーターを定義できないかなぁ

operator int*(int a){
 return &a;
}

文法上キャストのグローバルオペレータは定義できなかった気が・・・。



案その2:

変数aに自分自身のアドレスを入れる。

void func(int* a){
*a=12345;
}
int main(int argc,char* argv[])
{
int a=(int)&a;
func((int*)a);
printf("a=%d\n",(int)a);
return 0;
}

一応androidだと動くけれど、iphone5Sとiphone6だとたぶん動かない。


案その3:

変数aをintptr_tにする。

void func(int* a){
*a=12345;
}
int main(int argc,char* argv[])
{
intptr_t a=(intptr_t)&a;
func((int*)a);
printf("a=%d\n",(int)a);
return 0;
}

これで大体どんな環境でも動くじゃん。
64ビットのBIGエンディアンとかは動かない気もする。
ということはstrict aliasingルールに反する気も。
やっぱ、どうしてもあそこに&をつけたいけれど、女性とあの行だけは決して手をだしていけない。&をつけてはいけないにルール変更。


案その4:

void func(intptr_t* a){
*a=12345;
}
int main(int argc,char* argv[])
{
intptr_t a=(intptr_t)&a;
func((intptr_t*)a);
printf("a=%d\n",(int)a);
return 0;
}


やったーやっとこれできちんと動く。
やっぱり、どんな環境でもきちんと動くってとっても難しいです。

2014年12月15日月曜日

USB2BTでMac-miniとUSBキーボードを無線接続でつないでみた。

以前この日記で、CerevoのEnebrickを使ってUSBキーボードをBluetoothキーボードに変換してMac-miniで使う記事を書いたのですが、この製品、すぐにハングって使い物にならないので、別の製品を使うことにしました。




これが今回使うUSB2BT
http://bit-trade-one.co.jp/product/assemblydisk/adu2b01/
これを使うといろいろなUSBデバイスをBluetoothに変換できます。

Bluetoothって素人が開発すると結構OSによって繋がったり繋がらなかったりということが起こるのですが、この製品の開発者は大変優秀な方のようで、全く問題なくいろいろなOSで問題なくつながります。

これからはこれを使おうっと。



秋月電子で売っている「RN−42使用 Bluetooth無線モジュール評価キット」でシリアルポートを無線化してみた

マイコンとかのプログラム開発をするときに、PCとマイコンボードをだいたいUSBシリアル変換器でつなぎます。
この部分にケーブルがあるととても作業しづらいので、秋月電子で売っている「RN−42使用 Bluetooth無線モジュール評価キット」でシリアルポートを無線化してみました。

http://akizukidenshi.com/catalog/g/gK-07378/

といってもこのキットを買ってきて、最初の一回だけ評価モジュールの初期設定を行うだけです。
この評価モジュールとPCとをUSBケーブルでつないで、TeraTermなどからBluetoothの認証モードの設定を変更します。
シリアルから「$$$」と入力して設定モードに移行して、その次にコマンド「sa,2」を入力して認証モードを簡単認証モードに変更。
これだけでPCとマイコンが無線でシリアル通信ができます。
シリアルの信号線は評価モジュールのJ2コネクタの6番ピンと7番品に出ます。
あとはこのピンとグランドをマイコンのシリアルピンとグランドにつなぐだけ。

超簡単なんですが、いまのところ、Bluetoothのシリアルポートプロファイルにデフォルトで対応しているOSがWindows8.1以降とMacOSXしかないので、Windows7ユーザの人は接続できなくてはまります。

あと注意点としてはネットで評価モジュールのCTSとRTSを繋いだ方が良いと書いてあります。僕も以下のように改良してCTSとRTSをとりあえずつないでみました。





僕はこの試験をするためだけにWindows8.1を入れました。
一番高かったのがWindows8.1のライセンス料とは。。。


2014年12月11日木曜日

androidのbinderをWindows、Linux、MacOSXで実装する

毎回マニアックなプログラムのネタを書いていて、こんな日記を参考にする人がいるのかなぁと思っていたのですが、結構いるんですね。
さて、今日はプロセス間通信の移植の話。

androidのIPC(プロセス間通信)は/dev/bindrというバインダーデバイスを介して行われます。
androidではC/C++で実装されているネイティブサービスがこのバインダーを通してJavaレイヤと通信を行うのですが、このドライバー部分がネックとなりネイティブサービスをPC-Linux、WindowsやMacOSXで動かすことができません。
実機でしかネイティブサービスを開発できないのは大変不便なので、このbinderデバイス部分とC++の関連クラス一式をWindowsでエミュレーションして動くようにしてみました。
たぶんLinuxやMacOSXでも動きます。
dev/ashmemや複数のサービスを登録するなどはまだまだエミュレーションできませんが、数値や文字列だけを扱う関数ならば完全にエミュレーションで動かすことができます。
また、単独プロセスでサービスとクライアントを開発できるので、XcodeやVisualStudioなどの高度なIDEで開発を行うことができてとっても便利。

移植していて気付いたのですが、androidオリジナルのバインダーライブラリの設計の完成度の高さに感動します。
完全ロックフリーでマルチコアで高速動作するようになっているんですね。
どのような戦略で不完全なソースを高速化すれば良いのかとても勉強させられるコードです。

ソースはこちら
https://drive.google.com/file/d/0B5M9qMMg3tfQREJZekY0NDNNb00/view?usp=sharing

2014年12月9日火曜日

WindowsでUnixドメインソケットを使う

LinuxのプログラムをWindowsで動くように移植するとき、スレッドやミューテックなどは同等のAPIがWindowsにあるため、どうにかなるのですが、Unixドメインソケットの部分だけはなかなか同等のAPIがありません。

そこでみんなどうしているのかいろいろ調べてみました。

まずGoogle。
LocalBiChannnel.cppというのがAndroidのソースコードの中にあります。
たぶんローカルバイナリーチャンネルの略なのでしょう。
このファイルは1:1でローカルソケットを使うときはLinux、MacOSX、WindowsすべてのOSで動くので大変便利。Androidでは大体これを使っています。このやり方とても頭よいです。さすが。


それ以外の人はCygwinでUnixドメインソケットが使えるのでCygwinのローカルを使っているようです。

Cygwinを使わないで、Windowsで1:nのUnixドメインソケットを使いたい場合、よいライブラリがないので、Googleのコードを参考に変更して作ってみました。


---------------------------------------
localsocket_.c
---------------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "localsocket_.h"

#if defined(_WIN32)
# include <windows.h>
#endif

#if defined(unix) || defined(__APPLE__)
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/un.h>
#include <unistd.h>
#include <errno.h>
#endif



#ifndef SUN_LEN
/*
 * Our current set of ARM header files don't define this.
 */
# define SUN_LEN(ptr) ((size_t) (((struct sockaddr_un *) 0)->sun_path)        \
                      + strlen ((ptr)->sun_path))
#endif


static const unsigned long kInvalidHandle = (unsigned long) -1;
static const int kPipeSize = 4096;

/*
 * Destructor.  If we're the server side, we may need to clean up after
 * ourselves.
 */

void localsocket_close(localsocket_t* ls)
{
#ifdef _WIN32
if(ls==NULL)return;
    if (ls->mHandle != kInvalidHandle){
        FlushFileBuffers((HANDLE)(ls->mHandle));
        CloseHandle((HANDLE)(ls->mHandle));
}
    if (ls->mHandle2 != kInvalidHandle){
        FlushFileBuffers((HANDLE)(ls->mHandle2));
        CloseHandle((HANDLE)(ls->mHandle2));
}
free(ls);
#endif
#if defined(unix) || defined(__APPLE__)
if(ls==NULL)return;
    if (ls->mHandle != kInvalidHandle)
        close((int) (ls->mHandle));
    if (ls->mIsListener) {
        (void) unlink(ls->mFileName);
    }
free(ls);
#endif
}

void makeFilename(char* fileName, const char* name)
{
#ifdef _WIN32
static const char* kBasePath = "\\\\.\\pipe\\android-";
    strcpy(fileName, kBasePath);
    strcat(fileName, name);

#endif
#if defined(unix) || defined(__APPLE__)
    static const char* kBasePath = "/tmp/android-";
    strcpy(fileName, kBasePath);
    strcat(fileName, name);
#endif
}

static localsocket_t* alloc()
{
localsocket_t* ret;
ret=(localsocket_t*)malloc(sizeof(localsocket_t));
if(ret==NULL)return ret;
memset(ret,0,sizeof(localsocket_t));
ret->mHandle=kInvalidHandle;
ret->mHandle2=kInvalidHandle;
ret->mPort=1;

return ret;
}

localsocket_t* localsocket_create(const char* name)
{
#ifdef _WIN32
localsocket_t* ret;
if(name==NULL || strlen(name)>100)return NULL;

ret=alloc();
if(ret==NULL)return ret;

makeFilename(ret->mFileName,name);
return ret;
#endif
#if defined(unix) || defined(__APPLE__)
localsocket_t* ret;
    struct stat sb;
    int result = 0;
    int sock = -1;
    int cc;
    struct sockaddr_un addr;

if(name==NULL || strlen(name)>100)return NULL;

ret=alloc();
if(ret==NULL)return ret;

    makeFilename(ret->mFileName,name);

    cc = stat(ret->mFileName, &sb);
    if (cc < 0) {
        if (errno != ENOENT) {
free(ret);
return NULL;
        }
    } else {
        /* don't touch it if it's not a socket */
        if (!(S_ISSOCK(sb.st_mode))) {
free(ret);
return NULL;
   }

       /* remove the cruft */
        if (unlink(ret->mFileName) < 0) {
free(ret);
return NULL;
        }
    }

    sock = socket(AF_UNIX, SOCK_STREAM, 0);
    if (sock < 0) {
free(ret);
return NULL;
    }

    /* bind the socket; this creates the file on disk */
    strcpy(addr.sun_path, ret->mFileName);    // max 108 bytes
    addr.sun_family = AF_UNIX;
    cc = bind(sock, (struct sockaddr*) &addr, SUN_LEN(&addr));
    if (cc < 0) {
close(sock);
free(ret);
return NULL;
    }

    ret->mHandle = (unsigned long) sock;
    ret->mIsListener = 1;

return ret;

#endif
}

localsocket_t* localsocket_attach(const char* name)
{
#ifdef _WIN32
localsocket_t* ret;
HANDLE handle;
DWORD bytesRead;
int port;
char buf[16];
int ok;

if(name==NULL || strlen(name)>100)return NULL;
ret=alloc();
if(ret==NULL)return ret;

//
// open common pipe to fix port num
//

makeFilename(ret->mFileName,name);
do{
ok=WaitNamedPipe(ret->mFileName,1000*5);
if(ok==0){
// No calling CreateNamedPipe() API.
free(ret);
return NULL;
}
// If other thread called CreateFile() API, Windows returned INVALID_HANDLE_VALUE.
handle= CreateFile(
                ret->mFileName,                      // filename
                GENERIC_READ | GENERIC_WRITE,   // access
                0,                              // no sharing
                NULL,                           // security
                OPEN_EXISTING,                  // don't create
                0,                              // attributes
                NULL);                          // template
}while(handle == INVALID_HANDLE_VALUE);

buf[0]=0;
bytesRead=0;
ReadFile(handle, buf, 1, &bytesRead,NULL);
//printf("read=%d\n",bytesRead);
port=buf[0]&255;

CloseHandle(handle);
sprintf(buf,"-%d",port);

//
// open port 
//

strcat(ret->mFileName,buf);
//printf("port=%d   name=%s\n",port,ret->mFileName);
ok=WaitNamedPipe(ret->mFileName,1000*5);
if(ok==0){
// 
free(ret);
return NULL;
}
handle= CreateFile(
                ret->mFileName,                      // filename
                GENERIC_READ | GENERIC_WRITE,   // access
                0,                              // no sharing
                NULL,                           // security
                OPEN_EXISTING,                  // don't create
                0,                              // attributes
                NULL);                          // template
    if (handle == INVALID_HANDLE_VALUE) {
free(ret);
        return NULL;
    }
ret->mHandle=(unsigned long)handle;
return ret;
#endif
#if defined(unix) || defined(__APPLE__)
localsocket_t* ret;
    int result = 0;
    int sock = -1;
    int cc;
    struct sockaddr_un addr;

if(name==NULL || strlen(name)>100)return NULL;

ret=alloc();
if(ret==NULL)return ret;

    makeFilename(ret->mFileName,name);

    sock = socket(AF_UNIX, SOCK_STREAM, 0);
    if (sock < 0) {
free(ret);
return NULL;
    }

    /* connect to socket; fails if file doesn't exist */
    strcpy(addr.sun_path, ret->mFileName); 
// max 108 bytes
    addr.sun_family = AF_UNIX;
    cc = connect(sock, (struct sockaddr*) &addr, SUN_LEN(&addr));
    if (cc < 0) {
close(sock);
free(ret);
return NULL;
    }

ret->mHandle=sock;
    ret->mIsListener = 0;

    return ret;
#endif
}

localsocket_t* localsocket_listen(localsocket_t* ls)
{
return localsocket_listen_timeout(ls,0);
}

localsocket_t* localsocket_listen_timeout(localsocket_t* ls,int timeout)
{
#ifdef _WIN32
localsocket_t* ret;
    HANDLE hPipe;
    BOOL connected;
OVERLAPPED ol ={0};
char port[16];
DWORD bytesWritten;

if(ls==NULL)return NULL;

ret=alloc();
if(ret==NULL)return ret;

//
// open common port
//
if(ls->mHandle==kInvalidHandle){
hPipe = CreateNamedPipe(
                    ls->mFileName,              // unique pipe name
                    PIPE_ACCESS_DUPLEX      // open mode
| (timeout?FILE_FLAG_OVERLAPPED:0)
                        /*| FILE_FLAG_FIRST_PIPE_INSTANCE*/,
                    0,                      // pipe mode (byte, blocking)
                    3,                      // max instances
                    kPipeSize,              // output buffer
                    kPipeSize,              // input buffer
                    NMPWAIT_USE_DEFAULT_WAIT,   // client time-out
                    NULL);                  // security

   if (hPipe == 0) {
free(ret);
return NULL;
}
ls->mHandle=(unsigned long)hPipe;
}
//
// open first port
//
if(ls->mHandle2==kInvalidHandle){
int pp;
pp=ls->mPort & 255;
strcpy(ls->mFileName2,ls->mFileName);
sprintf(port,"-%d",pp);
strcat(ls->mFileName2,port);

//printf("port=%d  name=%s  name=%s\n",pp,ls->mFileName,ls->mFileName2);

hPipe = CreateNamedPipe(
                    ls->mFileName2,              // unique pipe name
                    PIPE_ACCESS_DUPLEX      // open mode
                        /*| FILE_FLAG_FIRST_PIPE_INSTANCE*/,
                    0,                      // pipe mode (byte, blocking)
                    3,                      // max instances
                    kPipeSize,              // output buffer
                    kPipeSize,              // input buffer
                    NMPWAIT_USE_DEFAULT_WAIT,   // client time-out
                    NULL);                  // security

   if (hPipe == 0) {
free(ret);
return NULL;
}
ls->mHandle2=(unsigned long)hPipe;
//printf("handle=%p\n",hPipe);
}

//
// connect
//
hPipe=(HANDLE)(ls->mHandle);

if(timeout){
connected=0;
ol.hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);
connected=ConnectNamedPipe(hPipe, &ol);
if(connected==0){
switch(GetLastError()){
case ERROR_PIPE_CONNECTED:
connected=TRUE;
break;
case ERROR_IO_PENDING:
if(WaitForSingleObject(ol.hEvent,timeout)==WAIT_OBJECT_0){
DWORD ig;
connected=GetOverlappedResult(hPipe,&ol,&ig,FALSE);
}else{
CancelIo(hPipe);
}
break;

}
}
CloseHandle(ol.hEvent);
}else{
   connected = ConnectNamedPipe(hPipe, NULL) ?
   TRUE : (GetLastError() == ERROR_PIPE_CONNECTED);
}
    if (connected==FALSE) {
free(ret);
return NULL;
}
//printf("connect step2\n");

// write port no.
port[0]=ls->mPort;
ls->mPort++;
WriteFile((HANDLE) (ls->mHandle), port, 1, &bytesWritten, NULL);
FlushFileBuffers((HANDLE) (ls->mHandle));
DisconnectNamedPipe(hPipe);

ConnectNamedPipe((HANDLE) (ls->mHandle2), NULL);

ret->mHandle=(unsigned long)(ls->mHandle2);
ls->mHandle2=(unsigned long)kInvalidHandle;

return ret;


#endif
#if defined(unix) || defined(__APPLE__)
localsocket_t* ret;
    int result = 0;
    struct sockaddr_un from;
    socklen_t fromlen;

    int sock, lsock;
    int cc;
fd_set readfds;
struct timeval tv;
int n;

if(ls==NULL)return NULL;

ret=(localsocket_t*)malloc(sizeof(localsocket_t));
if(ret==NULL)return ret;

lsock = (int) (ls->mHandle);
if(ls->mIsListened==0){
   cc = listen(lsock, 5);
if (cc < 0) {
free(ret);
return NULL;
}
ls->mIsListened=1;
}


if(timeout){
FD_ZERO(&readfds);
FD_SET(lsock, &readfds);

memset(&tv,0,sizeof(tv));
tv.tv_sec=timeout/1000;
tv.tv_usec=(timeout%1000)*1000;

n = select(FD_SETSIZE, &readfds, NULL, NULL, &tv);
if(n==0){
free(ret);
return NULL;
}
}
    fromlen = sizeof(from);     // not SUN_LEN()
    sock = accept(lsock, (struct sockaddr*) &from, &fromlen);
    if (sock < 0) {
free(ret);
return NULL;
    }

ret->mHandle=sock;
ret->mIsListener=0;
    return ret;
#endif
}


int localsocket_read(localsocket_t* ls,void* buf, int count)
{
#ifdef _WIN32
    DWORD totalBytesAvail = count;
    DWORD bytesRead;

if(ls==NULL || ls->mHandle==kInvalidHandle)return -1;

if (!ReadFile((HANDLE) (ls->mHandle), buf, totalBytesAvail, &bytesRead,
            NULL))
    {
        DWORD err = GetLastError();
        if (err == ERROR_HANDLE_EOF || err == ERROR_BROKEN_PIPE)
            return 0;
        return -1;
    }
    return (int) bytesRead;
#endif
#if defined(unix) || defined(__APPLE__)
    int cc;
if(ls==NULL)return -1;
    cc = read((int)(ls->mHandle), buf, count);
    if (cc < 0 && errno == EAGAIN)
        return 0;
    return cc;
#endif
}
int localsocket_write(localsocket_t* ls,const void* buf, int count)
{
#ifdef _WIN32
    DWORD bytesWritten;
if(ls==NULL || ls->mHandle==kInvalidHandle)return -1;

    if (!WriteFile((HANDLE) (ls->mHandle), buf, count, &bytesWritten, NULL)) {
        return -1;
    }
    return (int) bytesWritten;
#endif
#if defined(unix) || defined(__APPLE__)
    int cc;
if(ls==NULL)return -1;
    cc = write((int)(ls->mHandle), buf, count);
    if (cc < 0 && errno == EAGAIN)
        return 0;
    return cc;

#endif
}
int localsocket_readReady(localsocket_t* ls)
{
#ifdef _WIN32
    DWORD totalBytesAvail;
if(ls==NULL || ls->mHandle==kInvalidHandle)return -1;


    if (!PeekNamedPipe((HANDLE) (ls->mHandle), NULL, 0, NULL,
            &totalBytesAvail, NULL))
    {
        return 1;
    }

    return (totalBytesAvail != 0);
#endif
#if defined(unix) || defined(__APPLE__)
    errno = 0;
    fd_set readfds;
    struct timeval tv = { 0, 0 };
    int cc;

if(ls==NULL)return -1;

FD_ZERO(&readfds);
    FD_SET((int)(ls->mHandle), &readfds);

    cc = select((int)(ls->mHandle)+1, &readfds, NULL, NULL, &tv);
    if (cc < 0) {
        return 1;
    } else if (cc == 0) {
        /* timed out, nothing available */
        return 0;
    } else if (cc == 1) {
        /* our fd is ready */
        return 1;
    } else {
        return 1;
    }
#endif
}


---------------------------------------
localsocket_.h
---------------------------------------
#ifndef __LOCALSOCKET_H
#define __LOCALSOCKET_H

typedef struct st_local_socket{
    char       mFileName[128];
    char       mFileName2[128];
    int        mIsListener;
    unsigned long mHandle;
    unsigned long mHandle2;
int        mIsListened;
unsigned char mPort;
} localsocket_t;


#ifdef __cplusplus
extern "C" {
#endif

localsocket_t* localsocket_create(const char* name);
localsocket_t* localsocket_listen(localsocket_t* ls);
localsocket_t* localsocket_listen_timeout(localsocket_t* ls,int to);
localsocket_t* localsocket_attach(const char* name);

void localsocket_close(localsocket_t* ls);

int localsocket_read(localsocket_t* ls,void* buf, int count);
int localsocket_write(localsocket_t* ls,const void* buf, int count);
int localsocket_readReady(localsocket_t* ls);


#ifdef __cplusplus
}
#endif

#endif 


2014年9月16日火曜日

C++で無名関数を遅延実行する

C++でObjective-CのGCD(Grand Central Dispatch)のように無名関数を別スレッドで遅延実行することができれば便利なので、どうすればできるのか考えて実装してみました。

GCDをWindowsやLinuxのC++などで実行するライブラリ、XDispatchなどもあるのですが、C++にはARC(Automatic Reference Counting)が無い分、オブジェクトのdeleteなど、いろいろな制約があって微妙に使い勝手が違います。そこで少しずつGCDを自分で作ってGCDの技術を勉強してみようと思います。

まずC++のラムダ式から。C++のラムダ式だけだと見た目には似たようなことができるのですが、スレッッドやオブジェクトをARC管理していないので、オブジェクトの開放のタイミングと無名関数内でのキャプチャの解放タイミングが合わず、オブジェクトの相互更新とかスレッドのライフタイムの管理がなかなかうまくできません。

そこで以前紹介した、Googleがandroid用に作ったスマートポインタとC++のラムダ式を組み合わせて使ってみるとARCのようなことができるのではないかと思い、早速実験してみました。

やったー。予想通り値が5になりました。

あとは無名関数をスレッドで即実行するのではなくグローバルなキューに入れて実行すれば、CGDが簡単に作れるはず。


C++でスマートポインタとラムダ式を使うとオブジェクトのdeleteのタイミングを一切気にしなくてよいので、javaの無名クラスやObjective-Cのブロックみたいなことができて便利そう。


----------------
#include <stdio.h>

#include <functional>

//  Memory Debug
#if defined(_WIN32) && !defined(__GNUC__)
#define _CRTDBG_MAP_ALLOC
#include <crtdbg.h>
#endif /* _WIN32 */


#include "RefBase.h"
#include "Threads.h"


using namespace std;
using namespace android;


class MyThread :public Thread
{
public:
virtual ~MyThread();
MyThread(function<void()> f);
virtual bool threadLoop();
public:
function<void()> m_func;
};

MyThread::MyThread(function<void()>  func)
{
m_func=func;
}

MyThread::~MyThread()
{
}

bool MyThread::threadLoop()
{
if(m_func)m_func();
return false;
}


class A:public RefBase
{
public:
A();
virtual ~A();
void test();
void setVal(int a);
int getVal();
public:
int m_a;
};

A::A()
{
m_a=0;
printf("A::A\n");
}

A::~A()
{
printf("A::~A\n");
}
void A:: test()
{
}

void A::setVal(int a)
{
m_a=a;
}

int A::getVal()
{
return m_a;
}

int main()
{
#if defined(_WIN32) && !defined(__GNUC__)
// _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_WNDW);
// _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_WNDW);
// _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_WNDW);
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF|_CRTDBG_LEAK_CHECK_DF);
#endif


sp<MyThread> mt=NULL;
sp<A> c=NULL;
int i;

c=new A();
mt=new MyThread([=](){
int v;
v=c->getVal()+3;
c->setVal(v);
printf("v=%d\n",v);

});
i=c->getVal()+2;
c->setVal(i);
c=NULL;
mt->run();
mt->join();

return 0;
}






2014年9月12日金曜日

pthreadでスレッドの中断と再開をする

LinuxやmacOSのpthreadってスレッドを中断・再開するAPIがありません。このようなAPIがないのに、なぜかLinux上で動くJavaにはスレッドの中断や再開があります。

どうやって実装しているのかなぁと思っていたのですが、調べても見るとシグナルを使っていることがわかりました。

やっとやりかたができることがわかったのでサンプルを作ってみました。

これでlinux,windows、cygwin、ios、macos、androidで動く、中断再開可能なスレッドが作れます。
さらにスレッドからUI表示系のAPIを呼ぶときにmutexロックをすればiosのperformSelectorInBackground:withObject: のようなものも作れます。

iosのフレームワークはこのperformセレクターやGlobalCentralDispatchなどいろいろ便利な機能がたくさんあるのですが、その中に高度なテクニックが隠蔽されています。
いつもどうやって実装しているのかたまに調べているのですが、iosのフレームってすごい。

mythread2.c
--------------
#include <stdio.h>
#include <stdlib.h>
#if defined(unix) || defined(__APPLE__)
#include <unistd.h>
#endif
#ifdef _WIN32
#include <windows.h>
#endif

#include "mythread2.h"



#ifdef HAVE_PTHREAD_H
#define PTHREAD_CREATE(thread, attr, func, arg)                          \
(int)pthread_create((pthread_t *)(thread), (pthread_attr_t *)(attr), \
    (void * (*)(void *))(func), (void *)(arg))
#else
#ifdef _WIN32
#define BEGINTHREAD(src, stack, func, arg, flag, id)         \
(HANDLE)_beginthreadex((void *)(src), (unsigned)(stack), \
(unsigned (_stdcall *)(void *))(func), (void *)(arg),    \
(unsigned)(flag), (unsigned *)(id))
#endif
#endif



/* yomei fixed: translate __stdcall to __cdecl  */
#ifdef _WIN32
static unsigned __stdcall internal_start_thread( void * vp)
{
mythread2_t *t;
if(vp==NULL)return 0;

t=(mythread2_t *)vp;
return (unsigned)(((void * (*)(void *))(t->func))(t->arg));
}
#endif



#if defined(unix) || defined(__APPLE__)
void suspend_signal_handler( int sig ) {
    sigset_t signal_set;
    int no = 0;
 
    sigemptyset( &signal_set );
    sigaddset( &signal_set, SIGUSR2 );
    sigwait( &signal_set, &no );

    return;
}

void resume_signal_handler( int sig ) {
    return;
}
#endif

static void mythread2_init()
{
#if defined(unix) || defined(__APPLE__)
    struct sigaction    sigusr1;
    struct sigaction    sigusr2;
 #endif

static int flag_init=0;
if(flag_init)return;
flag_init=1;
//printf("mythread2_init\n");

#if defined(unix) || defined(__APPLE__)
    sigusr1.sa_flags = 0;
    sigusr1.sa_handler = suspend_signal_handler;
    sigemptyset( &sigusr1.sa_mask );

    sigusr2.sa_flags = 0;
    sigusr2.sa_handler = resume_signal_handler;
    sigemptyset( &sigusr2.sa_mask );

    sigaction( SIGUSR1, &sigusr1, 0 );
    sigaction( SIGUSR2, &sigusr2, 0 );
#endif

}


mythread2_t*
mythread2_create(void* func, void* arg)
{
mythread2_t *t;
mythread2_init();
t = (mythread2_t *)malloc(sizeof(mythread2_t));
if (!t)
{
goto fail0;
}
t->issuspend=0;
t->func = func;
t->arg = arg;

#ifdef HAVE_PTHREAD_H
if (PTHREAD_CREATE(&t->hthread, 0, func, arg))
{
goto fail1;
}
#else
#ifdef _WIN32
{
int id;
HANDLE hnd;
/* yomei fixed: this thread function must be a standard call.  */
hnd = BEGINTHREAD(0, 0, internal_start_thread, t, 0, &id);
if (!hnd)
{
goto fail1;
}
t->hthread = hnd;
}
#endif
#endif
return t;

fail1:
if (t)
{
free(t);
t = NULL;
}
fail0:
return t;
}



void
mythread2_join(mythread2_t* t)
{
mythread2_init();
if (!t)
{
goto fail;
}
#ifdef HAVE_PTHREAD_H
pthread_join(t->hthread, NULL);
free(t);
#else
#ifdef _WIN32
WaitForSingleObject(t->hthread, INFINITE);
CloseHandle(t->hthread);
free(t);
#endif
#endif

fail:
return;
}


void mythread2_sleep(int s)
{
#if defined(unix) || defined(__APPLE__)
sleep(s);
#endif
#ifdef _WIN32
Sleep(s*1000);
#endif
}


void
mythread2_suspend(mythread2_t* t)
{
if(t==NULL)return;
if(t->issuspend)return;
t->issuspend=1;

#if defined(unix) || defined(__APPLE__)
    pthread_kill( t->hthread, SIGUSR1 );
#endif
#if defined(_WIN32)
SuspendThread(t->hthread);
#endif
}

void
mythread2_resume(mythread2_t* t)
{
if(t==NULL)return;
if(t->issuspend==0)return;
t->issuspend=0;
#if defined(unix) || defined(__APPLE__)
    pthread_kill( t->hthread, SIGUSR2 );
#endif
#if defined(_WIN32)
ResumeThread(t->hthread);
#endif
}
--------------


mythread2.h
--------------
#ifndef __MYTHREAD2_H__
#define __MYTHREAD2_H__

#define __MYTHREAD2_H__VER "20140911"


#if defined(unix) || defined(__APPLE__)
/* define if use pthread */
#define HAVE_PTHREAD_H
#endif

#ifdef HAVE_PTHREAD_H
#include <pthread.h>
#include <string.h>
#else
#ifdef _WIN32
#include <windows.h>
#include <winbase.h>
#include <process.h>
#endif
#endif


#ifdef HAVE_PTHREAD_H
typedef pthread_t hthread_t;
#else
#ifdef _WIN32
typedef HANDLE hthread_t;
#endif
#endif



typedef struct _thread
{
hthread_t hthread;
void* func;
void* arg;
int issuspend;
} mythread2_t;


#ifdef __cplusplus
extern "C"
{
#endif

mythread2_t* mythread2_create(void* func, void *arg);
void mythread2_join(mythread2_t* t);
void mythread2_suspend(mythread2_t* t);
void mythread2_resume(mythread2_t* t);

void mythread2_sleep(int s);

#ifdef __cplusplus
}
#endif

#endif /* __MYTHREAD_H__ */

--------------




2014年9月8日月曜日

iPhoneでキーイベントを処理する

iPhoneにはキーボードがついていないのですが、BlueToothキーボードなどをつなげてキー入力をしたいときがあります。
iOS7以降でも動く、キーイベントを処理するプログラムのサンプルが世の中に無いので作ってみました。

これで、前回ブログに書いたコンソール表示するiphoneアプリと組み合わせていろいろなテストアプリがつくれます。



隠しファイルのmain.mでアプリケーションクラスを継承

main.m
−−−−−−−−−−−−−−−−
//
//  main.m
//  HelloUITextView
//
//  Created by yomei on 2014/09/04.
//  Copyright (c) 2014 yomei. All rights reserved.
//

#import <UIKit/UIKit.h>
#import "AppDelegate.h"
#import "MyUIApplication.h"

int main(int argc, char *argv[])
{
    @autoreleasepool {
        return UIApplicationMain(argc, argv, @"MyUIApplication", NSStringFromClass([AppDelegate class]));
    }
}
−−−−−−−−−−−−−−−

アプリケーションクラスのヘッダー

MyUIApplication.h
−−−−−−−−−−−−−−−−
#import <UIKit/UIKit.h>


@interface MyUIApplication : UIApplication
{
    NSMutableArray* _commands;
}

@end
−−−−−−−−−−−−−−−−

アプリケーションのインプリ
−−−−−−−−−−−−−−−−
#import "MyUIApplication.h"

#include "ViewController.h"

@implementation MyUIApplication

- (NSArray *)keyCommands {
    if (!_commands) {
        NSMutableArray *cmd = [NSMutableArray array];
        NSString* str=@"01234567890-^\xc2\xa5" @"qwertyuiop@[" @"asdfghjkl;:]" @"zxcvbnm,./_"
            @"!\"#$%&'()=~`{+*}<>?_| "
            @"\r\b";
        int i,len;
        UIKeyCommand *co;
        len=[str length];
        for(i=0;i<len;i++){
            co = [UIKeyCommand keyCommandWithInput:[str substringWithRange:NSMakeRange(i, 1)]                                                         modifierFlags:  0 //UIKeyModifierCommand
                action:@selector(handleKomoji:)];
            [cmd addObject:co];
        }
        
        str=@"abcdefghijklmnopqrstuvwxyz";
        len=[str length];
        for(i=0;i<len;i++){
            co = [UIKeyCommand keyCommandWithInput:[str substringWithRange:NSMakeRange(i, 1)]                                                         modifierFlags:  UIKeyModifierShift
                                            action:@selector(handleOomoji:)];
            [cmd addObject:co];
        }
        
        str=@"01234567890-^\xc2\xa5" @"qwertyuiop@[" @"asdfghjkl;:]" @"zxcvbnm,./_"
            @"!\"#$%&'()=~`{+*}<>?_| "
            @"\r\b";
        len=[str length];
        for(i=0;i<len;i++){
            co = [UIKeyCommand keyCommandWithInput:[str substringWithRange:NSMakeRange(i, 1)]                                                         modifierFlags: UIKeyModifierAlphaShift
                                            action:@selector(handleCapsKomoji:)];
            [cmd addObject:co];
        }

        str=@"abcdefghijklmnopqrstuvwxyz";
        len=[str length];
        for(i=0;i<len;i++){
            co = [UIKeyCommand keyCommandWithInput:[str substringWithRange:NSMakeRange(i, 1)]                                                         modifierFlags:  UIKeyModifierShift | UIKeyModifierAlphaShift
                                            action:@selector(handleCapsOomoji:)];
            [cmd addObject:co];
        }

        _commands = cmd;
    }
    return _commands;
}
- (void)handleKomoji:(UIKeyCommand *)keyCommand {
    NSString* str;
    const char* p;
    str=keyCommand.input;
    if([str isEqualToString:@"\xc2\xa5"])str=@"\\";
    p=[str UTF8String];
    [self onKey :(*p&255)];
}

- (void)handleOomoji:(UIKeyCommand *)keyCommand {
    NSString* str;
    char* p;
    str=keyCommand.input;
    p=[str UTF8String];
    if(*p>='a' && *p<='z')*p-=' ';
    [self onKey :(*p&255)];
}
- (void)handleCapsKomoji:(UIKeyCommand *)keyCommand {
    NSString* str;
    char* p;
    str=keyCommand.input;
    if([str isEqualToString:@"\xc2\xa5"])str=@"\\";
    p=[str UTF8String];
    if(*p>='a' && *p<='z')*p-=' ';
    [self onKey :(*p&255)];
}

- (void)handleCapsOomoji:(UIKeyCommand *)keyCommand {
    NSString* str;
    char* p;
    str=keyCommand.input;
    p=[str UTF8String];
    [self onKey :(*p&255)];
}


-(void)onKey:(int) keyCode
{
    // dispatch keyevent
    [ViewController onKey:keyCode];
}


@end
−−−−−−−−−−−−−−−−




2014年9月4日木曜日

iPhoneで「C言語入門」の「Hello Wrold」のプログラム書いてみた

iPhoneでC言語入門などの書籍に出てくるプログラムを作ろうとするとmain関数が隠蔽されていて見えなかったり、printfなどの関数はデバッグ出力には表示されるのですが、液晶画面の方には表示されなかったり。
これでは気軽にprintfデバッグでいろいろなコンソールプログラムやライブラリが作れません。
そこで、iPhone用のmain関数が実行できるソースコードを作ってみました。

どこまでObjective-CでどこからC言語にするか非常に迷います。
あとでもう少しコードを整理しよっと。



-----------
//
//  ViewController.m
//  HelloUITextView
//
//  Created by yomei on 2014/09/04.
//  Copyright (c) 2014 yomei. All rights reserved.
//

#ifdef __APPLE__
#import "ViewController.h"
#endif

#include <stdio.h>

#ifdef __APPLE__
#include <unistd.h>
#endif

#ifdef __APPLE__
#define main mymain
#endif


void mysleep(int s)
{
#ifdef __APPLE__
    sleep(s);
#endif
}

void print(char* str)
{
    if(str==NULL)return;
#ifdef __APPLE__
    [ViewController print:str];
#else
    printf("%s¥n",str);
#endif
}

int main(int argc,char* argv){
    int i;
    char buf[256];
    print("Hello iPhone World!");
    for(i=0;i<3;i++){
        sprintf(buf,"main i=%d",i);
        print(buf);
        mysleep(1);
    }
    return 0;
}



//
// object view
//
#ifdef __APPLE__

static UITextView* s_view=NULL;
static ViewController* s_viewcontroller=NULL;

@interface ViewController ()

@end

@implementation ViewController

- (void) print_uithread:(NSString*) str
{
    if(s_view==NULL)return;
    s_view.text=[s_view.text stringByAppendingFormat:@"%@\n",str ];
}
+ (void) print:(char*) str
{
    printf("%s\n",str);
    if(s_viewcontroller==NULL || str==NULL)return;
    NSString* str2 = [NSString stringWithCString: str encoding:NSUTF8StringEncoding];
    [s_viewcontroller performSelectorOnMainThread:@selector(print_uithread:) withObject:str2 waitUntilDone:NO];
}

- (void) run:(NSString*) val
{
    main(0,NULL);
}

- (void)viewDidLoad
{
    [super viewDidLoad];

    CGRect rect= self.view.bounds;
    UITextView* textview=[[UITextView alloc]initWithFrame:rect];
    textview.editable=NO;
    textview.font=[UIFont fontWithName:@"Helvetica" size:14];
    textview.backgroundColor=[UIColor blackColor];
    textview.textColor=[UIColor whiteColor];
    
    textview.text=@"";
    textview.text=[textview.text stringByAppendingFormat:@"%s\n","iPhone Console" ];
    textview.text=[textview.text stringByAppendingFormat:@"%s\n","revision 0.01" ];
    textview.text=[textview.text stringByAppendingFormat:@"%s\n","" ];
    
    s_view=textview;
    s_viewcontroller=self;
    
    //thread start
    [self performSelectorInBackground:@selector(run:) withObject:@""];
    
    // add view
    [self.view addSubview:textview];
    
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
}
-(void)viewDidUnload
{
    [super viewDidUnload];
    
    s_viewcontroller=NULL;
    s_view=NULL;
}

@end
#endif



--------
//
//  ViewController.h
//  HelloUITextView
//
//  Created by yomei on 2014/09/04.
//  Copyright (c) 2014 yomei. All rights reserved.
//

#import <UIKit/UIKit.h>

@interface ViewController : UIViewController

+ (void) print: (char*)str;

//@property (weak,nonatomic) IBOutlet UITextView* m_textview;


@end