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