rapidjson包裝與lua包裝以及兩者的交互

rapidjson包裝

rapidjson是有名的開源c++ json庫(kù)妓羊,其類java的API使得其易于使用,然而對(duì)于rapidjson中的setInt壳快,setString等等setXXX的函數(shù),以及getInt, getString等等getXXX的函數(shù),作者覺得太過于繁瑣畏腕,想到如果能將這些set和get封裝起來,僅僅使用一個(gè)函數(shù)接口來調(diào)用茉稠,那么代碼將會(huì)顯得很簡(jiǎn)潔描馅,維護(hù)起來也容易多了。

下面的代碼給出了一個(gè)簡(jiǎn)單的嘗試而线。

// for int
template <typename T>
typename std::enable_if<std::is_same<T, int>::value, void>::type
_set_value(rapidjson::Value* node, T value, rapidjson::Document& d)
{
    node->SetInt(value);
}

template <typename T>
typename std::enable_if<std::is_same<T, int>::value, void>::type
_get_value(rapidjson::Value* node, T& value)
{
    value = node->GetInt();
}  

// for string
template <typename T>
typename std::enable_if<std::is_same<T, std::string>::value, void>::type
    _set_value(rapidjson::Value* node, T value, rapidjson::Document& d)
{
    node->SetString(value.c_str(), value.length(), d.GetAllocator());
}

template <typename T>
typename std::enable_if<std::is_same<T, std::string>::value, void>::type
    _get_value(rapidjson::Value* node, T& value)
{
    value = std::string(node->GetString());
}

// for char *
template <typename T>
typename std::enable_if<std::is_same<T, const char *>::value, void>::type
    _set_value(rapidjson::Value* node, T value, rapidjson::Document& d)
{
    node->SetString(value, d.GetAllocator());
}

template <typename T>
typename std::enable_if<std::is_same<T, char *>::value, void>::type
_get_value(rapidjson::Value* node, T& value)
{
    value = node->GetString();
}

上面的代碼分別對(duì)數(shù)據(jù)的類型進(jìn)行了模板的特例化用來與rapidjson中setXXX, getXXX的數(shù)值類型保持一致铭污。這樣在使用時(shí),可以統(tǒng)一使用一個(gè)函數(shù)來替換setXXX, getXXX函數(shù)簇膀篮。比如:

rapidjson::Value* node;
int value = 10;
_set_value(node, 10, d);
_get_value(node, value);

根據(jù)上面模板函數(shù)嘹狞,可以擴(kuò)展跟多的函數(shù),比如:
<pre>
template <typename T>
int get_value(rapidjson::Document& d, const std::string& key, T& value)
{
using namespace std;
using namespace rapidjson;

vector<string> node_vec;
Value \*node = static_cast<Value *>(&d);

node_vec = split(key, '.');
for (int i = 0; i < node_vec.size(); i++) {
    auto itr = node->FindMember(node_vec[i].c_str());
    if (i == node_vec.size() - 1) {
        if (itr != 0) {
            Value* temp = &(itr->value);
            if (temp->IsObject())
                return -1;
            _get_value<T>(temp, value);
            return 0;
        } else {
            return -1;
        }
    } else {
        if (itr != 0) {
            node = &(itr->value);
            if (!node->IsObject()) {
                return -1;
            }
        }
        else {
            return -1;
        }
    }
}
return -1;

}
</pre>

這個(gè)模板函數(shù)通過一個(gè)使用"."(不包括引號(hào))作為分隔符的字符串往一個(gè)rapidjson結(jié)構(gòu)中獲取數(shù)據(jù)誓竿,如果這個(gè)字符串按"."分割并且順序與原字符串相同磅网,那么每個(gè)子字符串表示一個(gè)json樹中的節(jié)點(diǎn),最后一個(gè)子串為葉子節(jié)點(diǎn)筷屡,value為其值涧偷。

相應(yīng)地下面是set_value模板函數(shù)

<pre>
template <typename T>
int set_value(rapidjson::Document& d, const std::string& key, T value)
{
using namespace rapidjson;
using namespace std;

Value\* node = &d;
vector<string> node_vec;

node_vec = split(key, '.');
for (int i = 0; i < node_vec.size(); i++) {
    auto itr = node->FindMember(node_vec[i].c_str());
    if (i == node_vec.size() - 1) {
        if (itr != 0) {
            _set_value<T>(&(itr->value), value, d);
        } else {
            if (!node->IsObject()) {
                node->SetObject();
            }
            Value a;
            node->AddMember(node_vec[i].c_str(), d.GetAllocator(), a, d.GetAllocator());
            _set_value<T>(&((*node)[node_vec[i].c_str()]), value, d);
        }

    } else {
        if (itr != 0) {
            if (itr->value.IsObject()) {
                node = &(itr->value);
            } else {
                return -1;
            }
        } else {
            Value key_wrapper(node_vec[i].c_str(), d.GetAllocator());
            Value null_value;
            node->AddMember(key_wrapper, null_value, d.GetAllocator());
            node = &((*node)[node_vec[i].c_str()]);
            node->SetObject();
        }
    }
}
return 0;

}
</pre>

該函數(shù)會(huì)往json樹中根據(jù)指定的節(jié)點(diǎn)路徑去插入葉子節(jié)點(diǎn)的元素。

lua包裝

和rapidjson類似速蕊,在lua的c擴(kuò)展中有l(wèi)ua_getXXX, lua_pushXXX的函數(shù)簇嫂丙,使用類似的方法將這些函數(shù)封裝起來:
<pre>
// for int
template <typename T>
typename std::enable_if<std::is_same<T, int>::value, void>::type
lua_push(lua_State *L, T value)
{
lua_pushinteger(L, value);
}
template <typename T>
typename std::enable_if<std::is_same<T, int>::value, int>::type
lua_get(lua_State *L, T& value)
{
int isnum = 0;
value = lua_tointegerx(L, -1, &isnum);
return isnum != 1 ? -1 : 0;
}

// for double
template <typename T>
    typename std::enable_if<std::is_same<T, double>::value, void>::type
    lua_push(lua_State \*L, T value)
{
    lua_pushnumber(L, value);
}
template <typename T>
    typename std::enable_if<std::is_same<T, double>::value, int>::type
    lua_get(lua_State \*L, T& value)
{
    int isnum = 0;
    value = lua_tonumberx(L, -1, &isnum);
    return isnum != 1 ? -1 : 0;
}

// for char \* and string
template <typename T>
    typename std::enable_if<std::is_same<T, const char*>::value, void>::type
    lua_push(lua_State \*L, T value)
{
    lua_pushstring(L, value);
}
template <typename T>
    typename std::enable_if<std::is_same<T, std::string>::value, void>::type
    lua_push(lua_State \*L, T value)
{
    lua_pushstring(L, value.c_str());
}
template <typename T>
    typename std::enable_if<std::is_same<T, std::string>::value, int>::type
    lua_get(lua_State \*L, T& value)
{
    size_t len = 0;
    const char \*res = lua_tolstring(L, -1, &len);
    if (res == NULL) {
        return -1;
    } else {
        if (len == 0)
            value = std::string();
        else
            value = std::string(res, len);
        return 0;
    }
}

</pre>

對(duì)于char*類型的lua_getXXX函數(shù)建議不要包裝,這樣做是為了避免這種方式的使用规哲,為了確保取出的字符串是獨(dú)立內(nèi)存地址的跟啤,上面的實(shí)現(xiàn)實(shí)際上是建議使用string的接口來處理字符串。

使用上面的模板函數(shù)可以構(gòu)建更加靈活的模板函數(shù)唉锌,比如遞歸地往lua的棧中插入數(shù)據(jù):
<pre>
// recursively push value
// base function

void lua_push_recursive(lua_State \*L) {;}

template <typename T>
    void lua_push_recursive(lua_State \*L, T value)
{
    lua_push<T>(L, value);
}

template <typename T, typename... Args>
    void lua_push_recursive(lua_State *L, T value, Args... args)
{
    lua_push<T>(L, value);
    lua_push_recursive(L, args...);
}

</pre>

跟進(jìn)一步隅肥,利用上面的遞歸函數(shù),可以實(shí)現(xiàn)一個(gè)通用的調(diào)用lua函數(shù)的C的接口:
<pre>
template <typename T, typename... Args>
int CallLuaFunc(lua_State *L, T& res, const char* func, Args... args)
{
lua_getglobal(L, func); /* push function */
const int args_num = sizeof...(args);
if (args_num > 0) {
lua_push_recursive(L, std::forward<Args>(args)...);
}
if (lua_pcall(L, args_num, 1, 0) != 0) {
fprintf(stderr, "%s with args_num: %d\n", lua_tostring(L, -1), args_num);
lua_pop(L, 1); /* pop error message from the stack */
return -1;
}
if (lua_get<T>(L, res) != 0) {
lua_pop(L, 1);
return -1;
}
lua_pop(L, 1);
return 0;
}
</pre>

如果lua中的函數(shù)返回一個(gè)返回值袄简,那么可以通過上面的函數(shù)來調(diào)用該lua函數(shù)腥放,比如
<pre>
#test.lua
function add(x, y)
return x+y
end
#test.cpp
int value = 0;
lua_State *L;
CallLuaFunc(L, value, "add", 1, 2);
# value = 3
</pre>

使用lua操作rapidjson數(shù)據(jù)

一些業(yè)務(wù)需要靈活地操作json數(shù)據(jù),為了滿足這一要求绿语,使用lua通過c的API來操作rapidjson數(shù)據(jù)秃症,然后c++程序再通過調(diào)用lua函數(shù)獲取結(jié)果可以大大增加已經(jīng)部署的程序的靈活度候址。下面給出一個(gè)簡(jiǎn)單的封裝的類的定義:
<pre>
class LuaWrapper;
typedef int (LuaWrapper::*mem_func)(lua_State * L);
template <mem_func func>
int adapter(lua_State * L) {
LuaWrapper * ptr = *static_cast<LuaWrapper**>(lua_getextraspace(L));
return ((*ptr).*func)(L);
}

class LuaWrapper {
public:
    LuaWrapper(): doc(nullptr) {
        L_ = luaL_newstate();
        luaL_openlibs(L_);
        \*static_cast<LuaWrapper**>(lua_getextraspace(L_)) = this;
    }

    ~LuaWrapper() {
        lua_close(L_);
    }

    // a rapidjson document point is passed in, and this class is not
    // responsible for deleting this point
    int init(const char\* lua_file, rapidjson::Document *d) {
        doc = d;
        int error = luaL_dofile(L_, lua_file);
        if (error) {
            fprintf(stderr, "%s\n", lua_tostring(L_, -1));
            lua_pop(L_, 1);  /* pop error message from the stack */
            exit(-1);
        }

        const luaL_Reg regs[] = {
            { "get_value_str", &adapter<&LuaWrapper::_lua_get_value_str> },
            { "get_value_int", &adapter<&LuaWrapper::_lua_get_value_int> },
            { NULL, NULL }
        };
        //lua_newtable(L_); since the lua is quite simple, so remove table definition
        lua_pushglobaltable(L_);
        luaL_setfuncs(L_, regs, 0);
        return 0;
    }
    template <typename T, typename... Args>
    int GetProvider(T& res, const char\* func, Args... args) {
        assert(doc != nullptr);
        // the lua file must have a dispatcher function
        return lua_util::CallLuaFunc(L_,
                                     res,
                                     func,
                                     std::forward<Args>(args)...);
    }
private:
    int \_lua_get_value_str(lua_State \* L) {
        const char \*key = luaL_checkstring(L, 1);
        std::string str;
        int err = get_value(\*doc, key, str);
        if (err != 0) {
            lua_pushnil(L);
        } else {
            lua_push(L, str.c_str());
        }
        return 1;
    }
    int \_lua_get_value_int(lua_State \* L) {
        const char \*key = luaL_checkstring(L, 1);
        int val;
        int err = get_value(\*doc, key, val);
        if (err != 0) {
            lua_pushnil(L);
        } else {
            lua_push(L, val);
        }
        return 1;
    }
    rapidjson::Document \*doc;
    lua_State \*L_;
};

</pre>

在lua中注冊(cè)c的API需要滿足function<int(lua_State*)>這樣的函數(shù)標(biāo)識(shí),而類的成員函數(shù)無法滿足這樣的標(biāo)識(shí)种柑,為了兼容lua的API岗仑,這里使用了一個(gè)適配器int adapter(lua_State * L),適配器滿足lua的函數(shù)標(biāo)識(shí)的要求聚请,因此可以用來注冊(cè)成lua的c的API荠雕。

這樣,與lua的交互完全可以快速封裝在一個(gè)類里驶赏,從而為調(diào)用者提供簡(jiǎn)單易懂的接口炸卑。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市煤傍,隨后出現(xiàn)的幾起案子盖文,更是在濱河造成了極大的恐慌,老刑警劉巖患久,帶你破解...
    沈念sama閱讀 218,755評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件椅寺,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡蒋失,警方通過查閱死者的電腦和手機(jī)返帕,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來篙挽,“玉大人荆萤,你說我怎么就攤上這事∠晨ǎ” “怎么了链韭?”我有些...
    開封第一講書人閱讀 165,138評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)煮落。 經(jīng)常有香客問我敞峭,道長(zhǎng),這世上最難降的妖魔是什么蝉仇? 我笑而不...
    開封第一講書人閱讀 58,791評(píng)論 1 295
  • 正文 為了忘掉前任旋讹,我火速辦了婚禮,結(jié)果婚禮上轿衔,老公的妹妹穿的比我還像新娘沉迹。我一直安慰自己,他們只是感情好害驹,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評(píng)論 6 392
  • 文/花漫 我一把揭開白布鞭呕。 她就那樣靜靜地躺著,像睡著了一般宛官。 火紅的嫁衣襯著肌膚如雪葫松。 梳的紋絲不亂的頭發(fā)上瓦糕,一...
    開封第一講書人閱讀 51,631評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音进宝,去河邊找鬼刻坊。 笑死枷恕,一個(gè)胖子當(dāng)著我的面吹牛党晋,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播徐块,決...
    沈念sama閱讀 40,362評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼未玻,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了胡控?” 一聲冷哼從身側(cè)響起扳剿,我...
    開封第一講書人閱讀 39,264評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎昼激,沒想到半個(gè)月后庇绽,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,724評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡橙困,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年瞧掺,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片凡傅。...
    茶點(diǎn)故事閱讀 40,040評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡辟狈,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出夏跷,到底是詐尸還是另有隱情哼转,我是刑警寧澤,帶...
    沈念sama閱讀 35,742評(píng)論 5 346
  • 正文 年R本政府宣布槽华,位于F島的核電站壹蔓,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏猫态。R本人自食惡果不足惜佣蓉,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望懂鸵。 院中可真熱鬧偏螺,春花似錦、人聲如沸匆光。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)终息。三九已至夺巩,卻和暖如春贞让,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背柳譬。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評(píng)論 1 270
  • 我被黑心中介騙來泰國(guó)打工喳张, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人美澳。 一個(gè)月前我還...
    沈念sama閱讀 48,247評(píng)論 3 371
  • 正文 我出身青樓销部,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親制跟。 傳聞我的和親對(duì)象是個(gè)殘疾皇子舅桩,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 1. 寫在前面 很多時(shí)候我們都需要借助一些腳本語言來為我們實(shí)現(xiàn)一些動(dòng)態(tài)的配置,那么就會(huì)涉及到如何讓腳本語言跟原生語...
    杰嗒嗒的阿杰閱讀 3,436評(píng)論 9 31
  • Nginx API for Lua Introduction ngx.arg ngx.var.VARIABLE C...
    吃瓜的東閱讀 5,790評(píng)論 0 5
  • 當(dāng)在Lua和C之間交換數(shù)據(jù)時(shí)主要的問題是自動(dòng)回收與手動(dòng)回收內(nèi)存管理的不一致雨膨。因此擂涛,Lua 用一個(gè)抽象的棧在Lua與...
    luffier閱讀 2,653評(píng)論 0 3
  • 陪伴是什么?陪伴是最平常的參與聊记。 陪伴是什么撒妈?陪伴是最簡(jiǎn)單的分享。 陪伴是什么排监?陪伴是最長(zhǎng)情的告白狰右。 陪伴是什么?...
    輕塵2007閱讀 85評(píng)論 0 0
  • 哈哈,曝料曝料-嘴嘴兒是個(gè)話癆峭弟,有視頻為證哦附鸽!小嘴同志每日兢兢業(yè)業(yè),以清晨最盛瞒瘸!可以咿咿呀呀坷备、哼哼哈哈、嗚嗚啊啊情臭,...
    給你們_樂樂與開心閱讀 240評(píng)論 0 1