Rodhos Soft

備忘録を兼ねた技術的なメモです。Rofhos SoftではiOSアプリ開発を中心としてAndroid, Webサービス等の開発を承っております。まずはご相談下さい。

文字列受け渡し

class Hello {
    companion object {
        init {
            System.loadLibrary("Hello")
        }
    }

    external fun printHello()
    external fun printNative(str:String, len:Int);
}

fun main(args: Array<String>) {
    Hello().printHello()
    val str = "hello form kotoln"
    Hello().printNative(str, str.length)
}

でcpp側は

#include "Hello.h"
#include <iostream>
#include <string>
extern "C" {
    JNIEXPORT void JNICALL Java_Hello_printHello
    (JNIEnv *env, jclass obj) {
        std::cout << "hello cpp" << std::endl;
    }
    
    JNIEXPORT void JNICALL Java_Hello_printNative
    (JNIEnv *env, jclass obj, jstring str, jint len) {
        /// charに変換
        jboolean iscopy;
        const char* msgStr = env->GetStringUTFChars(str, &iscopy);
        char* copiedChars = strdup(msgStr);
        env->ReleaseStringUTFChars(str, msgStr);
        env->DeleteLocalRef(str);

        printf("%s\n", copiedChars);
    }
}

でいけた。 が、おそらくこの辺の文字コードの話がいるはず…。

Java とか Android (DEX) の MUTF-8 (Modified UTF-8) って何者よ?っていう話 - bearmini's blog

とか

  java, a unicode char will be encoded with 4 bytes (utf16).
  jstring will container characters  utf16
  std::string in c++ is essentially a string of bytes, not characters,not characters
 we have convert utf16 to bytes.

とか

  • 返却値
class Hello {
    companion object {
        init {
            System.loadLibrary("Hello")
        }
    }

    external fun printHello()
    external fun printNative(str:String, len:Int): Boolean;
}

fun main(args: Array<String>) {
    Hello().printHello()
    val str = "hello form kotoln"
    val ret = Hello().printNative(str, str.length)
    println("ret = " + ret)
}
#include "Hello.h"
#include <iostream>
#include <string>
extern "C" {
    JNIEXPORT void JNICALL Java_Hello_printHello
    (JNIEnv *env, jclass obj) {
        std::cout << "hello cpp" << std::endl;
    }
    
    JNIEXPORT jboolean JNICALL Java_Hello_printNative
    (JNIEnv *env, jclass obj, jstring str, jint len) {
        /// charに変換
        jboolean iscopy;
        const char* msgStr = env->GetStringUTFChars(str, &iscopy);
        char* copiedChars = strdup(msgStr);
        env->ReleaseStringUTFChars(str, msgStr);
        env->DeleteLocalRef(str);

        printf("%s\n", copiedChars);
        
        return true;
    }
}

cppでhelloworld

cのときと同様でhello.cppを作る。

#include "Hello.h"
#include <iostream>
extern "C" {
    JNIEXPORT void JNICALL Java_Hello_printHello
    (JNIEnv *env, jclass obj) {
        std::cout << "hello cpp" << std::endl;
    }
}

あとはコンパイル時に-libc++をつける。

 gcc -shared -I/Library/Java/JavaVirtualMachines/jdk1.8.0_144.jdk/Contents/Home/include -I/Library/Java/JavaVirtualMachines/jdk1.8.0_144.jdk/Contents/Home/include/darwin Hello.cpp -o libHello.dylib -lstdc++

他は同じ

cでhelloworld

java側を用意

public class Hello {
    static{
        System.out.println(java.library.path);

        System.loadLibrary("Hello");
    }
    
    static native void printHello();

    public static void main(String args[]){
        printHello();
    }
}

コンパイル

javac Hello.java

C側を作成、ヘッダ作成

javah Hello
#include <jni.h>
/* Header for class Hello */

#ifndef _Included_Hello
#define _Included_Hello
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     Hello
 * Method:    printHello
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_Hello_printHello
    (JNIEnv *, jclass);

#ifdef __cplusplus
}
#endif
#endif
#include "Hello.h"
#include <stdio.h>

JNIEXPORT void JNICALL Java_Hello_printHello
(JNIEnv *env, jclass obj) {
    printf("hello\n");
}

cをコンパイルするために含めるjniのヘッダーを入れるためjdkの場所を探す。

/usr/libexec/java_home -V

ライブラリ作成

gcc -shared -I/Library/Java/JavaVirtualMachines/jdk1.8.0_144.jdk/Contents/Home/include -I/Library/Java/JavaVirtualMachines/jdk1.8.0_144.jdk/Contents/Home/include/darwin Hello.c -o libHello.dylib

使うライブラリを実行時に指定して実行

 java -Djava.library.path=. Hello

LD_LIBRARY_PATHに設定すると良い?

チュートリアル2

さらに簡単な例

#include <rxcpp/rx.hpp>


rxcpp::observable<std::string> twice(std::string word) {
    return rxcpp::observable<>::just(word + word);
}

rxcpp::observable<int> length(std::string word) {
    int l = (int)word.length();
    return rxcpp::observable<>::just(l);
}
rxcpp::observable<std::string> times(std::string word, int times) {
    auto r = word;
    for (int i=0;i<times;i++) {
        r = r + word;
    }
    return rxcpp::observable<>::just(r);
}

void useRxCpp() {
    auto ob = rxcpp::observable<>::just("poi")
    .flat_map([=](std::string word){
        return length(word);
    }).as_dynamic()
    .flat_map([=](int length){
        return times("hoge", length);
    }).as_dynamic()
    .flat_map([=](std::string word) {
        return twice(word);
    }).as_dynamic();
    
    ob.subscribe([=](std::string word) {
        std::cout << word << std::endl;
    });
}

チュートリアル

一番簡単な使い方

    
    rxcpp::observable<int> obs = rxcpp::observable<>::create<int>
    (
     [=](rxcpp::subscriber<int> s) {
         s.on_next(100);
         s.on_next(200);
         s.on_next(300);
         s.on_completed();
     }
     );
    
    
    auto x = obs.flat_map([=](int x) {
        return rxcpp::observable<>::just(x * 1000);
    }).as_dynamic()
    .flat_map([=](int x) {
        return rxcpp::observable<>::just("hoge");
    }).as_dynamic();
    
    auto l = x.subscribe([=](std::string txt){
        std::cout << txt << std::endl;
    });
    
    std::async(std::launch::async, [l]() {
        usleep(5*1000*1000);
        std::cout << "**unsubscribe**" << std::endl;
        l.unsubscribe();
    });

variant 共用体

かなり便利なことがわかった。

#include <boost/variant.hpp>
#include <iostream>

int main(int argc, const char * argv[]) {
    // insert code here...
    std::cout << "Hello, World!\n";
    
    boost::variant<int, std::string> v;
    v = 100;

    boost::variant<int, std::string> s;
    s = "200";

    if (v.type() == typeid(int)) {
        int l = boost::get<int>(v);
        std::cout << l + 1000 << std::endl;
        
        int* k = boost::get<int>(&v);
        std::cout << *k + 2000 << std::endl;
        
    }
    
    return 0;
}

boost設定

Macの場合。brewでinstall
/usr/local/Cellar/boost/1.67.0_1
ここにincludeヘッダーとlibがあるのでXCodeのパスを通す。
Header Search Pathsにinclude
Library Search Pathsにlib
non-recursive

Other linker flagに -lboost_system設定

チュートリアル的な記述7

typeerase的なものを作ってみたがポインタ周りが怪しいので修正中

#include "TypeErase.hpp"
#include <iostream>
#include <map>

class AnyBase;
using AnyBase_sp = std::shared_ptr<AnyBase>;

class AnyBase {
public:
    AnyBase() = default;
    virtual ~AnyBase() = default;
    
    template<typename T>
    T get();
  
    static AnyBase_sp make(std::string x);
    static AnyBase_sp make(int x);


};

template <typename T>
class Any;

template <typename T>
using Any_sp = std::shared_ptr<Any<T>>;



template <typename T>
class Any : public AnyBase {
public:
    Any(std::string x) : hold(x){};
    Any(int x): hold(x){};
    ~Any() {};
    T hold;
    static Any_sp<T> make(std::string x) {
        auto r = std::make_shared<Any<T>>(x);
        return r;
    }
};

template <typename T>
T AnyBase::get() {
    return dynamic_cast<Any<T>*>(this)->hold;
}

AnyBase_sp AnyBase::make(std::string x) {
    auto a = std::make_shared<Any<decltype(x)>>(x);
    return a;
}

AnyBase_sp AnyBase::make(int x) {
    auto a = std::make_shared<Any<decltype(x)>>(x);
    return a;
}




class Storage {
public:
    Storage() = default;
    ~Storage() = default;
    
};



void useTypeTrace() {

    AnyBase_sp xx = AnyBase::make("xxxxx");
    AnyBase_sp yy = AnyBase::make(33333);


    
//    std::cout << "typeErase " << x->get<int>() << std::endl;
    std::cout << "typeErase " << xx->get<std::string>() << std::endl;
    std::cout << "typeErase " << yy->get<int>() << std::endl;


}

チュートリアル的な記述6

継承やテンプレートなどを色々使ってみた。

#include <iostream>

void hello() {
    std::cout << "hello/bye" << std::endl;
    
    std::string s;
    
    while (true) {
        std::cin >> s;

        if (s == "hello") {
            std::cout << "hello!" << std::endl;
        } else if (s == "bye") {
            std::cout << "bye!" << std::endl;
        } else {
            std::cout << "?" << std::endl;
        }
    }
}

class Name {
public:
    std::string name;
    Name(std::string name) {
        this->name = name;
    }
};

template <typename T>
class EchoSkill {
public:
    void echo(T x);
};

template <>
void EchoSkill<std::string>::echo(std::string x) {
    std::cout << "echo: " << x << std::endl;
}

template <>
void EchoSkill<int>::echo(int x) {
    std::cout << "echo int: " << x << std::endl;
}

class Counter : public Name, public EchoSkill<int>, public EchoSkill<std::string> {
public:
    Counter(std::string name) : Name(name) {
        
    }
    ~Counter(){}
    void count() {
        countNum++;
    }
    int getCount() {
        return countNum;
    }
private:
    int countNum = 0;
};

std::string question(std::string txt) {
    std::string s;
    std::cout << txt << std::endl;
    std::cin >> s;
    return s;
}

void hello2() {
    
    typedef std::shared_ptr<Counter> Counter_sptr;
    auto counter = std::make_shared<Counter>("hoge");
    
    while (true) {
        std::string s = question("hello/bye/repCount/name");
        
        if (s == "hello") {
            std::cout << "hello! " << counter->name <<"." << std::endl;
            counter->count();
        } else if (s == "bye") {
            counter->EchoSkill<std::string>::echo("bye!");
            break;
        } else if (s == "name") {
            counter->name = question("input name");
        } else if (s == "repCount") {
            counter->EchoSkill<int>::echo(counter->getCount());
        } else {
            std::cout << "?" << std::endl;
        }
    }
}

チュートリアル的な記述5

クラス、構造体

// class デフォルトのアクセス指定子がprivate
// struct デフォルトのアクセウ指定子がpublic

struct Animal;
struct Man2;

struct Animal {
    int age = 10;
    virtual void attack();
};

struct Man2 : Animal {
    typedef std:: shared_ptr<std::string> string_ptr;
    string_ptr name_ptr;
    Man2(std::string name);
    ~Man2();
    static void staticFunc();
    void hello();
    void func1() &; //
    void func1() &&; // 右辺値参照の際
    void constFunction() const;
};


Man2::Man2(std::string name) {
    this->name_ptr = std::make_shared<std::string>(name);
}

Man2::~Man2() {
    
}

void Man2::staticFunc() {
    std::cout << "staticFunc" << std::endl;
}

void Man2::hello() {
    std::cout << "hello" << std::endl;
}

void Man2::func1() & {
    std::cout << "func1 &" << std::endl;
}
    
void Man2::func1() && {
    std::cout << "func1 &&" << std::endl;
}
    
void Man2::Animal::attack() {
    std::cout << "attack" << std::endl;
}

void useMan2() {
    auto man2 = new Man2("aaa");
    int a = man2->age;
    Man2::staticFunc();
    man2->staticFunc();
    man2->hello();
    man2->func1();
    Man2("cddd").func1();
}

チュートリアル的な記述4

ファイル操作

#include <iostream>
#include <fstream>


void fileSample() {
    // file
    std::ofstream ofs("test.txt");
    if (!ofs) {
        std::cerr << "[error]open file" << std::endl;
        std::exit(1);
    }
    ofs << "Hello, World\n123" << std::endl; // auto close
    
    std::ifstream ifs("test.txt");
    if (!ifs) {
        std::cerr << "[error]open file for ifstream." << std::endl;
        std::exit(1);
    }
    
    std::string bufstr;
    ifs >> bufstr;
    
    std::cout << bufstr << std::endl; //[note] output is Hello,
    
    
    std::ifstream ifs2("test.txt");
    if (!ifs2) {
        std::cerr << "[error]open file for ifstream." << std::endl;
        std::exit(1);
    }
    std::string bufstr2;
    getline(ifs2, bufstr2);
    std::cout << bufstr2 << std::endl; //[note] output is Hello, World
    
    getline(ifs2, bufstr2);
    std::cout << bufstr2 << std::endl; //[note] output is 123
    
    if (ifs2.eof()) {
        std::cout << "eof" << std::endl; //[note] output is 123
    }
}

チュートリアル的な記述3

変数

#include <sstream>
#include <iomanip>
#include <fstream>
void basicVariable() {
    const int c = 10;
    
    // ref
    int r = 0;
    int& r2 = r;
    r2 = 100;
    std::cout << r << std::endl; // 100
    
    // 構造体
    Point p;
    p.x = 10;
    p.y = 10;
    std::cout << p.d2() <<std::endl;
    
    // enum
    // enum
    ColorType1 type1 = ColorType1::RED;
    ColorType2 type2 = ColorType2::RED;
    ColorType3 type3 = ColorType3::RED;
    
    ColorType1 type4 = RED;
    //    ColorType2 type5 = RED; できない
    //    ColorType3 type6 = RED; できない
    
    std::cout << type1 << std::endl;
    std::cout << static_cast<int>(type2) << std::endl;
    std::cout << static_cast<int>(type3) << std::endl;
    std::cout << type4 << std::endl;
    
    // auto 型推論
    auto x = "x";
    
    // 文字列
    std::string z = "ac";
    auto y = 3.0;
    auto z1 = z + z;
    auto z2 = x + z;
    auto z3 = z + x;
    std::string z5 = z.append(x); // acx
    std::string z6 = z.append(z); // acxacx
    
    z.empty();// empty check
    
    //auto z4 = x + x; //できない。 const char*
    std::cout << x << std::endl;
    std::cout << y << std::endl;
    std::cout << z << std::endl; // acxacx
    std::cout << z5 << std::endl;// acx
    std::cout << z6 << std::endl;// acxacx
    
    // decltype
    int sample = 100;
    decltype(sample) sample2 = 200; // Type of sample2 is Int.
    decltype(getValue()) sample3 = 3.3;//  Type of sample3 is Int.
    std::cout << sample << sample2 << sample3 << std::endl;
    
    
    // escape
    std::string stresc = "a\nb\nc";
    std::string stresc2 = R"(a
    b
    c)";
    std::cout << stresc << std::endl;
    std::cout << stresc2 << std::endl;
    
    // null pointer
    int* a = nullptr;
    if (a == nullptr) {
        std::cout << "nullptr" << std::endl;
    }
}

文字列

#include <iostream>
void stringUse() {
    std::string x = "aaa";
    std::string y = "bbb";
    std::string z = x + y;
    
    std::cout << z << std::endl;

}

ベクトル

#include <iostream>
#include <vector>
void vectorUse() {
    
    std::vector<int> v = {1,5,10,100};
    
    v[2] = 100;
    
    v.push_back(50);
    
    /// もっと簡単に表示してしたい
    for (int x: v) {
        std::cout << x << std::endl;
    }
    
    std::vector<int> v2 = {1,3,2,5};
    
    /// +は使えないのだろうか。 C++にカテゴリ拡張はない。
    v2.insert(v2.end(), v.begin(), v.end());
    
    
    /// coutが面倒だ。。
    std::cout << "v2" << std::endl;

    for (int x: v2) {
        std::cout << x << std::endl;
    }

}