Rokiのチラ裏

学生による学習のログ

ランダムアクセス可能な全ての範囲を無駄なくベクトル演算する

※ 2018/3/11 ライブラリ整理より当機能は削除した。
一々コンテナ毎にExpression templateを記述するのはバカバカしいので、書いてしまった。

ご覧の通り、Expression templateによって式構造を保存して遅延実行を行うため、範囲の無駄な一時オブジェクトは一切生成されない。以下のように使える。

#include<srook/range/pipe_algorithm/vector_calculator.hpp>
#include<srook/range/adaptor/copied.hpp>
#include<boost/range/algorithm_ext/iota.hpp>
#include<array>
#include<vector>

int main()
{
    using namespace srook::pipealgo::vector_calculator;

    std::array<int,4> ar1,ar2,ar3;
    std::array<int,5> result1;
    
    boost::iota(ar1,1); // ar1: 1 2 3 4
    boost::iota(ar2,*std::next(std::end(ar1),-1)); // ar2: 4 5 6 7
    boost::iota(ar3,*std::next(std::end(ar2),-1)); // ar3: 7 8 9 10

    result1 | srook::pipealgo::assign(ar1-ar2+ar3); // result1: 4 5 6 7 0
    
    std::vector<int> 
        v1 = ar1 | srook::adaptors::copied,
        v2 = ar2 | srook::adaptors::copied,
        v3 = ar3 | srook::adaptors::copied,
        result2;

    result2 | srook::pipealgo::assign(ar1+ar2+ar3,std::back_inserter(result2)); // result2: 12 15 18 21

    std::vector<int> result3 = result2 | srook::pipealgo::assign(ar1+ar2+ar3); // result3: 12 15 18 21
}

全ての範囲で利用できるようにするため、こちらが定義した演算子をADLで探索させるためにsrook::pipealgo::vector_calculatorをusing namespaceする必要がある。グローバル名前空間を汚さないように大量の要件が定義されている他、当名前空間をusing namespaceして公開される機能は演算子のみなので、然程影響がないと思われる。

  • result1の結果で示している通り、出力側の範囲の要素が入力側の範囲の要素よりも多かった場合、0で埋める。
  • result2で示している通り、出力側の任意のイテレータを指定する事が可能である。
  • result3で示している通り、返却値は出力側の範囲の参照であるため、その返却値を利用したコピーコンストラクトが可能である。

python de 同一メゾッド名の型チェック付き呼び分け

バイト中、以下のようなコードを見つけた。

def something1(x,y):
    print 'something1'
def something2(x,y):
    print 'something2'
def something3(x,y,z):
    print 'something3'

def invoke(*args):
    argc=len(args)
    exp_message='not match function'

    if argc==3:
        something3(args[0],args[1],args[2])
    elif argc==2:
        if isinstance(args[0],int) and isinstance(args[1],str):
            something2(args[0],args[1])
        elif isinstance(args[0],int) and  isinstance(args[1],float):
            something1(args[0],args[1])
        else:
            raise Exception,exp_message
    else:
        raise Exception,exp_message

invoke("hoge",42,4.2)
invoke(42,"hoge")
invoke(42,4.2)

うーん。凄まじいハードコードである。やりたい事はオーバーロードだろう。私はpythonの専門家ではないので、更に良い解決法はあるのだろうが、取り敢えず以下のように引数の違いで呼び分ける上で型チェックをするようにした。

import sys

exp_message="not match function"

class Invoker(object):
    def __init__(self):
        self.functions=[]

    def __call__(self,*args,**kwargs):
        for function in self.functions:
            if len(args)==function.func_code.co_argcount:
                return function(*args)
        raise Exception,exp_message

def invoke_impl(func):
    hs=sys._getframe(1).f_globals.get(func.__name__)
    if hs is None:
        hs=Invoker()
    hs.functions.append(func)

    return hs;

@invoke_impl
def something(x,y,z):
    if not (isinstance(x,str) and isinstance(y,int) and isinstance(z,float)):
        raise Exception,exp_message
    print 'something3'

@invoke_impl
def something(x,y):
    if isinstance(x,int) and isinstance(y,str):
        print 'something2'
    elif isinstance(x,int) and isinstance(y,float):
        print 'something1'
    else:
        raise Exception,exp_message

something("hoge",42,4.2)
something(42,"hoge")
something(42,4.2)

型チェックはやはりisinstanceを使う他はないだろうか。

コンパイル時のシーケンス生成は時が経って簡単になった

偶然見つけた以下のエントリ。

cpplover.blogspot.jp saito.hatenablog.jp

2010年...あれから時は経ち、いとも簡単にコンパイル時のシーケンス生成は簡単になった。

#include<srook/mpl/constant_sequence/equ_sequence.hpp>
#include<srook/mpl/constant_sequence/algorithm/transfer_array.hpp>
#include<iostream>

for(auto&& value:srook::constant_sequence::transfer_array<srook::constant_sequence::make_equ_sequence_t<5>>::value)
        std::cout<<value<<std::endl;

make_equ_sequenceとかいう適当な命名の再起関数によって数値5から0,1,2,2,3,3,3,4,4,4,4,5,5,5,5,5のシーケンスを生成し、transfer_arrayによって配列(std::array)に変換している。

良き時代になったものだ。

Siriから自宅サーバー監視カメラ(motion)をオンオフするまで

今回もお家ハックネタ。ラズパイとかでも同じ事ができると思う。 motionについては過去のエントリを参照してほしい。

さて、取り敢えずまずはphpからmotionを起動したり殺したりしなければならない。そのためにはroot権限が必要になる。これには、apacheからのmotion実行に対しての操作はパスワードが不要である旨をvisudoに明記する必要がある。*1

% sudo visudo
Defaults visiblepw
www-data ALL=(ALL) NOPASSWD:/usr/bin/motion;/usr/bin/killall

で、phpファイルであるが、motionプロセスが立ち上がっていれば殺し、立ち上がっていなければ生成させるようにする。取り敢えず以下のようにしている。

<?php
$output=shell_exec('ps alx | grep motion | grep -v grep | grep -v \'php motion\'');

if(strpos($output,'motion')!==false){
    `sudo killall motion`;
}
else if(strpos($output,'motion')===false){
    `sudo motion`;
}
?>

以上で、HTTP GETリクエストを送ればmotionが付いたり消えたりするようになる。

$ curl -X GET "http://server/motion.php"

さて、次にHTTPリクエストを送るだけのアプリを作る。といっても本当にHTTPリクエストを送るだけなので簡単。レスポンスが返ってきた瞬間にアプリを即終了させる。

import UIKit

class URLSessionGetClient {
    func get(url urlString: String) {
        let url = URL(string: urlString)
        let task = URLSession.shared.dataTask(with: url!) { data, response, error in
            if let _ = data, let response = response {
                print(response)
                exit(EXIT_SUCCESS);
            } else {
                print(error ?? "Error")
                exit(EXIT_SUCCESS);
            }
        }
        task.resume()
    }
    
}

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        let urlSessionGetClient = URLSessionGetClient()
        urlSessionGetClient.get(url: "http://192.168.12.2/motion.php")
    }
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
}

適当にアイコンを設定。アイコンは一つ素材を用意すればconvertコマンドで別サイズの画像を生成できる。

% convert icon.png -resize 120x120 appiconset/iPhone-App-7-10-60pt@2x.png
% convert icon.png -resize 180x180 appiconset/iPhone-App-7-10-60pt@3x.png

後は、連絡先などでニックネームを決めてやるとSiriから任意の呼称で起動できるようになる。以下のように動く。

総括

お家ハックは楽しい。

*1:セキュリティ的に公開サーバーなどではこのような事をしない方が良い。

Line Notify APIで遊ぶ

仕事関連の作業や自作ライブラリ開発等の合間に、息抜きとして何かお家ハック的な別の事で遊びたかったのだが、少し良い遊び道具が見つかったので一つ。

Line NotifyというLine社提供のapiがある。これが何なのかについては、公式ページに詳しくあるので参照してほしい。

簡単に言えば、OAuth2 による認証を行い取得したアクセストークンからAPIを叩く事でグループ内に通知を送りつける事ができるAPIである。使い方は、開発ブログにあるように、curlを使えばとても簡単。

curl -X POST -H 'Authorization: Bearer [access_token]' -F 'message=foobar' https://  
notify-api.line.me/api/notify

APIに関するドキュメントもシンプルで整備されている。

さて、このAPIを使ってグループ内に通知を送る。用途は…まあなんというか個人的な話なのだが、執筆時でもうすぐ二年ぐらい付き合っている彼女と私は同棲をしている。付き合ってから一月に一度、互いにまあ初心を忘れぬよう、今日で何ヶ月付き合っているのだなという話をするのだが、最近になって私が忘れたりするようになった。これはいかんので、一月に一度、手元の端末に通知を送れたりできれば良い(?)。そういう用途を満たすには、適当にシェルスクリプトを記述して自宅サーバのcronに登録しておけば問題ない。ただまあ、どうせならバイナリにしておこうと思ったので、そうする。libcurlを使えば簡単だろう。

#include<iostream>
#include<fstream>
#include<iterator>
#include<boost/optional.hpp>
#include<curl/curl.h>

using namespace std::string_literals;

struct line_curler{
    explicit line_curler(const char* token,const char* str)
        :line_curler(token,str,boost::none,boost::none)
    {}

    explicit line_curler(
            const char* token,
            const char* str,
            boost::optional<const char*> image_url,
            boost::optional<const char*> thumbnail=
            "https://placehold.jp/150x150.png"
    )
        :mes_(str),
        post_data("message="s+mes_),
        thumbnail_(thumbnail?"imageThumbnail="s+std::string(thumbnail.value()):""s),
        image_url_(image_url?"imageFullsize="s+std::string(image_url.value()):""s),
        handler(curl_easy_init()),
        slist(nullptr)
    {
        std::string token_="Authorization: Bearer "s+std::string(token);

        slist=curl_slist_append(slist,token_.c_str());
        curl_easy_setopt(handler,CURLOPT_URL,"https://notify-api.line.me/api/notify");
        curl_easy_setopt(handler,CURLOPT_SSL_VERIFYPEER,false);
        curl_easy_setopt(handler,CURLOPT_HTTPHEADER,slist);
        curl_easy_setopt(handler,CURLOPT_POST,true);
        
        if(!image_url_.empty()){
            post_data+="&"s+thumbnail_+"&"s+image_url_;
            curl_easy_setopt(handler,CURLOPT_POSTFIELDS,post_data.c_str());
        }else{
            curl_easy_setopt(handler,CURLOPT_POSTFIELDS,post_data.c_str());
            curl_easy_setopt(handler,CURLOPT_POSTFIELDSIZE,post_data.size());
        }
    }
    
    void perform()const{curl_easy_perform(handler);}

    ~line_curler()
    {
        curl_slist_free_all(slist);
        curl_easy_cleanup(handler);
    }
private:
    const std::string mes_;
    std::string post_data;
    const std::string thumbnail_;
    const std::string image_url_;

    CURL* handler;
    curl_slist* slist;
};

std::string& trimer(std::string& str)
{
    const char* trimlist="\t\v\r\n";
    const std::string::size_type left=str.find_first_not_of(trimlist);
    if(left!=std::string::npos)
        str=str.substr(left,str.find_last_not_of(trimlist)-left+1);    
    return str;
}

struct token_reader{
    explicit token_reader(const char* filename):file_(boost::none)
    {
        std::ifstream ifs(filename);        
        if(ifs){
            std::string all_((std::istreambuf_iterator<char>(ifs)),std::istreambuf_iterator<char>());
            file_=trimer(all_);
        }
    }

    token_reader(const token_reader&)=default;
    token_reader(token_reader&&)=default;

    operator bool()const{return bool(file_);}
    operator const char*(){return file_?file_.value().c_str():nullptr;}
protected:
    boost::optional<std::string> file_;
};

template<class... Args>
auto exec_line_curl(Args&&... args) -> decltype(std::enable_if_t<sizeof...(args)<4>())
{
    line_curler lc(std::forward<Args>(args)...);
    lc.perform();
}

int main(const int argc,const char* argv[])
{
    if(argc<2)return -1;

    token_reader token("line_notify_api");

    if(token and argc==2)
        exec_line_curl(std::move(token),argv[1]);
    else if(token and argc==3)
        exec_line_curl(std::move(token),argv[1],argv[2]);
}

ビルドには-lcurlが必要。バイナリと同じディレクトリのline_notify_apiというファイル名からアクセストークンを読み込んで、単にapiを叩くだけだ。 以下のように使える。

./a.out 横から失礼致します。 http://www.dotup.org/uploda/www.dotup.org1173928.jpg

後は、このプログラムをcronで設定しておけば良いだろう。

Line Notify APIは、GithubやIFTTT、Mackerelなどと連携させる事ができるようなので、お家ハック的な意味ではまだ遊びようがありそうではある。取り敢えず、IFTTTから天気情報を流す設定はしてみた。

というわけで仕事に戻る

Tuple-based for loops

あまり体調が良くないのだが、適度に頭を使わなくても書けるネタを見つけたのでアップロード。P0589R0にインスパイアされて、そういえば書いておくと楽だろうなと今頃思ったので自分でも書いた。tupleの展開には、いつも再起したりindex_sequence使ったりapplyしたりしていたが。

P0589R0には、本提案によってinitializer-listとparameter packの相互作用により、複数の型を持つパラメータパックなどを適用できるようになるとある*1。...とここでinitializer_listの話題があったので、どうせなら対応しようと思い対応してみた。また、for_each中にカウントしたい場合があるかもしれないとも思ったので、そのようなものも。まぁ、こういうのは結構前からあるとは思うのだが、一応こう使える。

std::vector<int> v{10,20,30};
const auto disp=[](const auto&){/* */}; 
const auto counter=[](const auto&,std::size_t){/* */}; // valueとカウンター値

srook::for_each(v.begin(),v.end(),disp); // 普通のfor_each
srook::for_each(v,disp); // range base for_each
srook::for_each(std::make_tuple(42,'a',42.5f),disp); // tuple
srook::for_each({10,20,30},disp); // initializer_list

srook::for_each(srook::make_counter(v.begin(),v.end()),counter); // カウント.デフォルト値は0
srook::for_each(srook::make_counter(v.begin(),v.end(),5),counter); // 5からカウントを始める
srook::for_each(srook::make_counter(v),counter); // rangeで指定
srook::for_each(srook::make_counter(std::make_tuple(42,'a',42.5f)),counter); // tuple
srook::for_each(srook::make_counter({10,20,30}),counter); // initializer_list

まあ、boost.fusionを使えば良いという気はする。

*1:P0589R0- Interaction with initializer lists and parameter packs