Rokiのチラ裏

学生による学習のログ

cereal + boost json_parserによるjsonファイル入出力の一例

題名は何だかやたら大々的だがそんな大した事はしていない。

あるところで、シリアル番号を生成したいという案件が来たというような話を伺った。まあ、ちょいちょいとやれば簡単に実装できるとの話だったので、「そうですね」と私は答えた。「そうですね」と答えた以上、ある程度書けなくてはならない。そんなわけで、cerealとboost json_parserを使って一応雑に帰りの電車の中で書くだけ書いた。*1

  • シリアル番号は連番。一応ユーザー数が露見しないよう可逆スクランブルしてその値を16進値としてユーザーに渡す。
  • スクランブル値とデスクランブルのための値をjsonで保持する。
  • 可逆スクランブルのアルゴリズムは簡潔的にするためMersenne twisterで得られた一定の値を単に足している。変えたくなったら関数オブジェクトでも渡せるような設計にすればおk
#include<iostream>
#include<random>
#include<functional>
#include<fstream>
#include<cereal/cereal.hpp>
#include<cereal/archives/json.hpp>
#include<boost/property_tree/ptree.hpp>
#include<boost/property_tree/json_parser.hpp>

template<class Jsonable>
struct Serializable final:public Jsonable{
    using Jsonable::Jsonable;
    template<class U>
    void serialize(U& archive)
    {
        archive(CEREAL_NVP(Jsonable::pretty_scrambler_key),CEREAL_NVP(Jsonable::scrambler));
    }

    template<class Stream>
    void json_output(Stream& ss,const char* s="root")
    {
        cereal::JSONOutputArchive oarchive(ss);
        oarchive(cereal::make_nvp(s,*this));
    }
    template<class Stream>
    void json_input(Stream& ss,const char* s="root")
    {
        cereal::JSONInputArchive iarchive(ss);
        iarchive(cereal::make_nvp(s,*this));
    }
};

struct serial_manager{
    serial_manager(unsigned long long int value)
        :pretty_scrambler_key(std::mt19937()()),
        scrambler(0)
    {
        scrambler=value+pretty_scrambler_key;
    }
    serial_manager(std::size_t pretty_scrambler_key,std::size_t scrambler)
        :pretty_scrambler_key(pretty_scrambler_key),
        scrambler(scrambler)
    {}
    std::size_t get_serial_key()const{return scrambler;}
    std::size_t get_pretty_scrambler_key()const{return pretty_scrambler_key;}
    std::size_t get_native()const{return scrambler-pretty_scrambler_key;}
protected:
    std::size_t pretty_scrambler_key,scrambler;
private:
    static void* operator new(std::size_t)=delete;
    static void* operator new(std::size_t,void*)=delete;
};

unsigned long long int input()
{
    unsigned long long int user=0;
    std::cout<<"user number >> :";
    std::cin>>user;
    return user;
}

int main()
{
    auto user=input(); // データーベースからユーザー数を取得という妄想(実際のインプリメントは単なる標準入力)

    Serializable<serial_manager> s(user);
    std::cout<<"Your serial key: "<<std::hex<<s.get_serial_key()<<std::endl; // エンドユーザーへ渡す値
    // 登録操作
    std::ofstream ofs(std::to_string(user));
    s.json_output(ofs);
    // 読み込み操作
    boost::property_tree::ptree pt;
    boost::property_tree::read_json(std::to_string(user),pt);
    auto key=pt.get_optional<unsigned long long int>("root.Jsonable::pretty_scrambler_key");
    auto scrambler=pt.get_optional<unsigned long long int>("root.Jsonable::scrambler");   
    
    if(key && scrambler){
        auto real_serial=scrambler.value()-key.value(); // 実際の連番
        std::cout<<real_serial<<std::endl;
    }
}
% ./a.out
user number >> :1
Your serial key: d091bb5d
1

% ./a.out
user number >> :1200
Your serial key: d091c00c
1200

以下サボりポイント

そもそも、こういうものはphpなどで書くのが一般的か。

わざわざブログに上げるまでもなかった気がするので時間が経過したら削除するかもしれない。

*1:boost serializeを使わないのは単に好みで