XParam Tutorial

XParam Tutorial



چکیده

این نوشتار با ارایه مثال هایی به شرح و تضیح کتابخانه XParam و قابلیت ها و امکانات آن می پردازد.

کلید واژه ها

XParam, XML

1 مقدمه

امروزه اکثر نرم افزارها بازیابی، ذخیره سازی و دریافت داده های خود را در قالب XML انجام می دهند. توسعه دهندگان ما شروع به نوشتن XParam به زبان ++C به منظور فراهم آوردن کتابهخانه ای که کار با XML را تسهیل می بخشد، کردند. با استفاده از XParam، توسعه دهندگان می توانند کلاسسی را تعریف کنند و سپس آن را در قالب XML جاپ نمایند یا در فایلی ذخیره کنند ویا XMLی را از رشته یا فایلی در کلاسی بارگذاری کنند.

بعلاوه، XParam روش نوینی در طراحی مبتنی بر شی گرا به نام XObject معرفی می کند. XObject جهان واقعی را به صورت مجموعه ای از اشیا و ارتباطات آن ها تعریف می کند. این روش موجب کاهش پیچیدگی طراحی و افزایش انعطاف پذیری و پایداری سیستم می گردد.

XParam چیست؟

XParam کتابخانه ای بر پایه کتابخانه ++XML می باشد و شامل مجموعه ای از کلاس هاست که ساختار و محتوای (نوع داده) XML را تعریف می کنند.

3 کلاس ها

3.1  کلاس XSingleParam

به منظور تعریف شی مجرد (tشی ای که شامل شی دیگری نیست و تنها دارای مقدار می باشد) از این کلاس استفاده می شود. از آنجا که این کلاس، کلاسی عام می باشد، معمولا بطور مستقیم از آن استفاده نمی شود بلکه از کلاس های مشتق شده از آن استفاده می شود. برای مثال، اطلاعات مربوط به کارت شبکه در قالب زیر آمده است.

 

3.1.1  مثال: مشخصات کارت شبکه

<device>eth1</device>

<ipv4>192٫168٫0٫1</ipv4>

<ipv6>3fee::1</ipv6>

<rx_packets>57347</rx_packets>

<tx_packets>48936</tx_packets>

همان طور که مشاهده می کنید، مقادیر tag ها بصورت رشته، IP و عدد صحیح می باشند که به ترتیب کلاس های XTextParam، IPv4Param، IPv6Param و XIntParam بدین منظور پیاده سازی شده اند. کد Nic.cpp چگونگی استفاده از این کلاس ها را نشان می دهد.

 

3.1.2  کد Nic.cpp

#include <iostream>

using std::cout;

using std::endl;

 

#include <xparam/sparam.hpp>

#include <xparam/xparam.hpp>

using namespace xparam;

 

int main()

{

XTextParam device("device");

IPv4Param ipv4("ipv4");

IPv6Param ipv6("ipv6");

XIntParam<XUInt> recievedPackets("rx_packets", 0, -1);

XIntParam<XUInt> transferredPackets("tx_packets", 0, -1);

 

try {

device = "eth1";

ipv4 = "192.168.0.1";

ipv6 = "3fee::1";

recievedPackets = 57347;

transferredPackets = 48936;

} catch (Exception &exception) {

cout << "ERROR: " << exception.what() << endl;

}

cout << device.xml() << endl;

cout << ipv4.xml() << endl;

cout << ipv6.xml() << endl;

cout << recievedPackets.xml() << endl;

cout << transferredPackets.xml() << endl;

 

return 0;

}

همان طور که مشاهده می کنید سازنده ی هر کلاس دارای حداقل یک ورودی می باشد که عنوان tag را مشخض می کند. کلاس XIntParam دارای دو ورودی دیگر می باشد که به ترتیب مقدار حداقل و حداکثر tag مربوطه را مشخص می کند. در صورتی که مقدار حداکثر از مقدار حداقل کمتر باشد، بازه ی مقدار tag بررسی نمی گردد.

با استفاده از عملگر انتساب می توان به هر یک از متغیرها مقداری را انتساب داد. در صورتی که مقدار منتساب شده نامعتبر باشد، exceptionی از نوع کلاس Exception که توسط کتابخانه ی XParam پیاده سازی و عرضه شده است، پرتاب می گردد. توسط تابع what کلاس Exception می توانید از متن exception پرتاب شده آگاه شوید.

به منظور تبدیل کلاس به رشته XML، از تابع xml استفاده نمایید.

حالا زمان آن رسیده است تا کد فوق را ترجمه و اجرا نمایید، جهت ترجمه برنامه، فرمان زیر را اجرا نمایید.

g++ --std=c++0x nic.cpp -o nic `pkg-config libxparam-0.1 --cflags –libs`

با اجرای برنامه، خروجی همچون مثال: مشخصمشخصات کارت شبکه را مشاهده خواهید کرد.

 

3.2  کلاس XMixParam

از این کلاس به منظور تعریف اشیای مرکب (شی ای که خود شامل شی های دیگر است) استفاده می شود. برای مثال، اطلاعات مربوط به کاربر شبکه در زیر آمده است.

 

3.2.1  مثال: مشخصات کاربرشبکه

<user>

<type>local</type>

<full_name>Example User</full_name>

<username>exam</username>

<password>1a79a4d60de6718e8e5b326e338ae533</password>

<enabled>yes</enabled>

<login_date>2013/11/26 15:01:27</login_date>

</user>

ابتدا بهتر است کدی که منجر ایجاد XML فوق می شود را مشاهده کنیم.

 

3.2.2  کد user.cppp

#include <iostream>

using std::cout;

using std::endl;

 

#include "xparam/sparam.hpp"

#include "xparam/xparam.hpp"

using namespace xparam;

 

class UserType

{

public:

enum {

ADMINISTRATOR,

LOCAL,

REMOTE,

SUPPORT,

LOG,

MAX

};

 

static const string typeString[MAX];

};

 

const string UserType::typeString[MAX] = {

"administrator",

"local",

"remote",

"support",

"log",

};

 

class User : public XMixParam

{

public:

User() :

XMixParam("user"),

type("type", UserType::LOCAL),

fullName("full_name"),

username("username"),

password("password"),

enabled("enabled"),

loginDate("login_date")

{

addParam(&type);

addParam(&fullName);

addParam(&username);

addParam(&password);

addParam(&enabled);

addParam(&loginDate);

 

password.set_runtime();

enabled.yes();

loginDate.now();

}

void set_fullName(const string _fullName)

{

fullName = _fullName;

}

void set_username(const string _username)

{

username = _username;

}

void set_password(const string _password)

{

password = _password;

password.encrypt_md5();

}

 

private:

XEnumParam<UserType> type;

XTextParam fullName;

XTextParam username;

CryptoParam password;

BoolParam enabled;

DateTime loginDate;

};

 

int main()

{

User user;

 

user.set_fullName("Example User");

user.set_username("exam");

user.set_password("example");

cout << user.xml(true, 8, true);

 

return 0;

}

همان طور که در کد فوق مشاهده می کننید کلاس User از XMixParam ارث برده است که این امر منجر به تعریف tag مرکب می گردد. این کلاس دارای هفت متغیر عضو می باشد که هر کدام از آن ها خود یک tag مجرد می باشند. جهت افزودن هر کدام از این tagهای مجرد به tag مرکب از تابع addParam استفاده می شود که در اینجا در سازنده ی کلاس User فراخوانی شده است.

متغیر عضو type از نوع XEnumParam می باشد و بیانگر آن است که این متغیر یک enum می باشدو مقادیر آن توسط کلاس UserType مشخص گردیده است. سازنده ی این متغیر علاوه بر نام tag، ورودی دیگری که بیانگر مقدار پیش فرض می باشد را می پذیرد.

کلاس UserType دارای یک enum (جهت افزایش خوانایی برنامه) و یک متغیر به نام typeString که آرایه ای از رشته هاست و مقادیر رشته ای مجاز برای متغیر type را دارا می باشد. باید توجه داشت که نام و نوع متغیر typestring ثابت است.

متغیر عضو password ازنوع CryptoParam می باشد و در واقع XTextParam با قابلیت رمزنگاری می باشد. فراخوانی تابع encrypt_md5 موجب تبدیل مقدار tag به کد MD5 می گردد. همان طور که مشاهده می نمایید تابع set_runtime برای این ممتغیر فراخوانی شده است که منجر به عدم ایجادXML متغیر توسط تابع xml (با مقادیر پیش فرض) می گردد.

متغیر عضو enabled ازنوع BoolParam می باشد و در واقع XEnumParamی است با مقادیر آن دو به دو باهم در تضاد می باشند مانند: بله و خیر، روشن و خاموش، فعال و غیرفعال و ... .

متغیر عضو loginDate از نوع DateTime می باشد و مقدار آن تاریخ و ساعت در فالب ثانیه/دقیقه/ساعت روز/ماه/سال می باشد. تابع now تاریخ و ساعت جاری را به عنوان مقدار متغیر تنظیم می کند.

 

3.3  کلاس XSetParam

از این کلاس به منظور تعریف اشیای مجموعه ای (مجموعه ای از اشیایای هم نوع) استفاده می شود. برای مثال، اطلاعات رسد serverهای یک سازمان در ادامه آمده است.

 

3.3.1  مثال: اطلاعات رسد serverهای یک سازمان

<servers>

<server>

<ip>3fee::1</ip>

<uptime>1759412</uptime>

<cpu_usage>27.5</cpu_usage>

<ram_usage>44</ram_usage>

</server>

<server>

<ip>192.168.0.1/24</ip>

<uptime>687958</uptime>

<cpu_usage>56.2</cpu_usage>

<ram_usage>70.8</ram_usage>

</server>

</servers>

کد مربوط به XML فوق در ادامه آمده است.

3.3.2  کد servers.cpp

#include <iostream>

using std::cout;

using std::endl;

 

#include "xparam/sparam.hpp"

#include "xparam/xparam.hpp"

using namespace xparam;

 

class Server : public XMixParam

{

public:

Server() :

XMixParam("server"),

ip("ip"),

uptime("uptime", 0, -1),

cpuUsage("cpu_usage", 0, 100),

ramUsage("ram_usage", 0, 100)

{

addParam(&ip);

addParam(&uptime);

addParam(&cpuUsage);

addParam(&ramUsage);

}

bool key(string &_key)

{

_key = ip.value();

 

return true;

}

 

IPxParam ip;

XIntParam<XULong> uptime;

XIntParam<XFloat> cpuUsage;

XIntParam<XFloat> ramUsage;

};

class Servers : public XSetParam<Server,string>

{

public:

Servers() :

XSetParam<Server,string>("servers")

{

enable_smap();

}

};

 

int main()

{

Server server;

Servers servers;

 

try {

server.ip = "3fee::1";

server.uptime = 1759412;

server.cpuUsage = 27.5f;

server.ramUsage = 44.0f;

servers.addT(server);

server.ip = "192.168.0.1/24";

server.uptime = 687958;

server.cpuUsage = 56.2f;

server.ramUsage = 70.8f;

servers.addT(server);

} catch (Exception &exception) {

cout << exception.what() << endl;

 

return -1;

}

 

cout << servers.xml(false, 8, true);

 

return 0;

}

کلاس Server از XMixParam ارث برده است و tagی مرکب، شامل چهار tag مجرد می باشد.

متغیر عضو ip از نوع IPxParam می باشد که IPv4 و IPv6 را پشتیبانی می کند. تابع value مقدار متغیر ip را در قالب رشته برمی گرداند. لازم به دکر است تابع value تابعی است مجازی که توسط هر کلاس پیاده سازی شده است و خروجی آن از نوع رشته می باشد.

کلاس Servers از XSetParam ارث برده است و tagی مجموعه ای، از کلاس Server می باشد. کلاس XSetParam یک نمونه (template) می باشد که دو نام نوع به نام های T و key را می پذیرد. نام نوع T کلاسی که بیانگر tag (مجرد یا مرکب) عضو کلاس XSetParam را مشخص می کند و نام نوع key کلید این مجموعه را که می تواند int (پیش فرض) ویا string باشد را مشخص می کند. در صورت فراخوانی تابع enable_smap مقدار کلید چهت حصول اطمینان از منحصربفرد بودن کلید بررسی می شود و با فراخوانی تابع disable_smap مقدار کلید بررسی نخواهد شد.

مقدار کلید توسط تابع key پیاده سازی شده توسط کلاس Server مشخص می گردد که در اینجا IP مربوط به هر server می باشد و بدین معنی است که نمی توان دو server با IPهای یکسان را به فهرست serverها افزود.

به منظور افزودن tag جدید به tag مجموعه ای servers از تابع addT استفاده می نماییم و به منظور حذف tagی، از تابع del که ورودی آن کلید tag مورد نظر می باشد، استفاده می نمایید.

 

3.4 کلاس XISetParam

از این کلاس به منظور تعریف اشیاای مجموعه ای وارث (شی اای شامل اشیای متفاوت که از کلاس مشترکی ارث برده اند) اصتفاده می شود. به عنوان مثال، به XML زیر توجه نمایید.

 

3.4.1 مثال: فهرست کاربران شبکه

<user_list>

<user>

<username>admin</username>

<password>21232f297a57a5a743894a0e4a801fc3</password>

<legal_mac>3f:4e:bc:fd:6a:8f</legal_mac>

</user>

<user>

<username>remote</username>

<password>2c18e486683a3db1e645ad8523223b72</password>

<domain>example</domain>

<legal_ip_list>

<ip>192.168.0.1</ip>

<ip>3fee::1</ip>

</legal_ip_list>

</user>

</user_list>

همان طور که مشاهده می نمایید این فهرست شامل اطلاعات دو کاربر است با ساختار XML متفاوت. در ادامه کد مربوط به XML فوق آمده است.

 

3.4.2 کد user_list.hpp

#include <iostream>

using std::cout;

using std::endl;

 

#include "xparam/sparam.hpp"

#include "xparam/xparam.hpp"

using namespace xparam;

 

class User;

 

class UserRole : public XMixParam

{

public:

enum Role {

ADMINISTRATOR,

REMOTE,

MAX

};

 

UserRole() :

XMixParam("user"),

role("role", ADMINISTRATOR)

{

addParam(&role);

}

void set_role(const int _role)

{

role = _role;

}

User *newT() throw (Exception);

 

public:

static const string typeString[MAX];

 

private:

XEnumParam<UserRole> role;

};

 

class User : public XMixParam

{

public:

typedef UserRole Type;

 

User() :

XMixParam("user"),

role("role"),

username("username"),

password("password")

{

addParam(&role);

addParam(&username);

addParam(&password);

 

password.set_runtime();

}

virtual void type(Type &_type) const

{

}

void set_role(const string _role)

{

role = _role;

}

string get_username()

{

return username.value();

}

void set_username(const string _username)

{

username = _username;

}

void set_password(const string _password)

{

password = _password;

password.encrypt_md5();

}

 

private:

XTextParam role;

XTextParam username;

CryptoParam password;

};

 

class Administrator : public User

{

public:

Administrator() :

legalMAC("legal_mac")

{

addParam(&legalMAC);

 

set_role("administrator");

}

virtual void type(Type &_type) const

{

_type.set_role(UserRole::ADMINISTRATOR);

}

void set_legalMAC(const string mac)

{

legalMAC = mac;

}

private:

MACAddressParam legalMAC;

};

 

class RemoteUser : public User

{

public:

RemoteUser() :

domain("domain"),

legalIPList("legal_ip_list")

{

addParam(&domain);

addParam(&legalIPList);

 

set_role("remote");

}

virtual void type(Type &_type) const

{

_type.set_role(UserRole::REMOTE);

}

void set_domain(const string _domain)

{

domain = _domain;

}

void addlegalIP(const string ip)

{

IPxParam ipx("ip");

 

ipx = ip;

legalIPList.addT(ipx);

}

 

private:

XTextParam domain;

IPxList legalIPList;

};

 

class UserList : public XISetParam<User>

{

public:

UserList() :

XISetParam<User>("user_list")

{

}

};

کلاس UserRole شامل enumی است که نقش کاربر را تعریف می کند و کلاس های مشتق شده از آن به منظور تعیین نقش خود استفاده می نمایند. توضیح تابع newT را به بعد موکول می کنیم.

کلاس User شامل تابع مجازی type می باشد که این تابع توسط کلاس های مشتق شده آز آن پیاده سازی خواهد شد. توجه داشته باشید که این تابع باید بدین صورت تعریف گردد زیرا کلاس XISetParam از آن استفاده می کند.

کلاس Administrator تابع مجازی type را پیاده سازی نموده و نقش خود را به عنوان ADMINISTRATOR تعربف کرده است. این کلاس دارای متغیر عضو legalMAC از نوع MACAddressParam می باشد.

کلاس RemoteUser همچون کلاس Administrator نقش خود را با پیاده سازی تاب مجازی type تعریف کرده است. این کلاس دارای دو متغیر عضو به نام های domain و legalIPList می باشد که به ترتیب از نوع XTextParam و IPxList می باشد.

 

3.4.3 کد user_list.cpp

#include "user_list.hpp"

 

const string UserRole::typeString[MAX] = {

"administrator",

"remote"

};

 

User *UserRole::newT() throw (Exception)

{

if (role.get_value() == ADMINISTRATOR)

return new Administrator;

if (role.get_value() == REMOTE)

return new RemoteUser;

 

throw Exception("Undefined user", TracePoint("user"));

}

 

int main()

{

Administrator administrator;

RemoteUser remoteUser;

UserList userList;

 

try {

administrator.set_username("admin");

administrator.set_password("admin");

administrator.set_legalMAC("3f:4e:bc:fd:6a:8f");

userList.addT(administrator);

remoteUser.set_username("remote");

remoteUser.set_password("remote");

remoteUser.set_domain("example");

remoteUser.addlegalIP("192.168.0.1/24");

remoteUser.addlegalIP("3fee::1");

userList.addT(remoteUser);

} catch (Exception &exception) {

cout << exception.what() << endl;

}

cout << userList.xml(true, 8, true);

 

return 0;

}

تابع newT با توجه به مقدار متغیر role که توسط تابع type پیاده سازی شده توسط کلاس های مشتق شده، شی ای را ایجاد کرده و برگرداند.

 

3.5  کلاس XListParam

این کلاس همانند کلاس XISetParam می باشد ولی با این تفاوت که عناصر XISetParam در vector دخیره می شوند ولی عناصر XlistParam در Xlist.

 

3.5.1  XList چیست؟

لیستی پیوندی ویژه‌ای است بدینصورت که در صورت حذف عنصر از لیست، مرورهای آینده لیست قابلیت مشاهده آن را نخواهند داشت اما عنصر نیز تا زمانی که فعالیتی بر روی آن انجام میشود از حافظه حذف نخواههد شد. قابل ذکر است که فعالیت در حال انجام بر روی عنصر حذف شده امکان عقب و جلو شدن در لیست را خواهد داشت.

فعالیت یا اشاره‌گر به عنصر با iterator شبیه‌سازی میشود.

عنصر زمانی بطور کامل حذف می گردد که هیچ اشاره گری (iterator) بدان وجود نداشته باشد.

در این لیست ویژه، مرورهای(Read) همزمان با یک حذف/اضافه(Write) امکان پذیر است. این لیست را میتوان شبیه لیستهای RCU در کرنل تصور کرد.

 

3.5.2 کد user_xlist.hpp

#include <iostream>

using std::cout;

using std::endl;

 

#include "xparam/sparam.hpp"

#include "xparam/xparam.hpp"

using namespace xparam;

 

class User;

 

class UserRole : public XMixParam

{

public:

enum Role {

ADMINISTRATOR,

REMOTE,

MAX

};

 

UserRole() :

XMixParam("user"),

role("role", ADMINISTRATOR)

{

addParam(&role);

}

void set_role(const int _role)

{

role = _role;

}

User *newT() throw (Exception);

 

public:

static const string typeString[MAX];

 

private:

XEnumParam<UserRole> role;

};

 

class User : public XMixParam

{

public:

typedef UserRole Type;

 

User() :

XMixParam("user"),

role("role"),

username("username"),

password("password")

{

addParam(&role);

addParam(&username);

addParam(&password);

 

password.set_runtime();

}

virtual void type(Type &_type) const

{

}

bool key(string &_key)

{

_key = username.value();

 

return true;

}

void set_role(const string _role)

{

role = _role;

}

string get_username()

{

return username.value();

}

void set_username(const string _username)

{

username = _username;

}

void set_password(const string _password)

{

password = _password;

password.encrypt_md5();

}

 

private:

XTextParam role;

XTextParam username;

CryptoParam password;

};

 

class Administrator : public User

{

public:

Administrator() :

legalMAC("legal_mac")

{

addParam(&legalMAC);

 

set_role("administrator");

}

virtual void type(Type &_type) const

{

_type.set_role(UserRole::ADMINISTRATOR);

}

void set_legalMAC(const string mac)

{

legalMAC = mac;

}

 

private:

MACAddressParam legalMAC;

};

 

class RemoteUser : public User

{

public:

RemoteUser() :

domain("domain"),

legalIPList("legal_ip_list")

{

addParam(&domain);

addParam(&legalIPList);

 

set_role("remote");

}

virtual void type(Type &_type) const

{

_type.set_role(UserRole::REMOTE);

}

void set_domain(const string _domain)

{

domain = _domain;

}

void addlegalIP(const string ip)

{

IPxParam ipx("ip");

 

ipx = ip;

legalIPList.addT(ipx);

}

 

private:

XTextParam domain;

IPxList legalIPList;

};

 

class UserList : public XListParam<User,string>

{

public:

typedef XListParam<User,string>::iterator ListIterator;

 

UserList() :

XListParam<User,string>("user_list")

{

enable_smap();

}

};

همان طور که مشاهده می کنید، کد فوق هماند کد user_list.hpp می باشد با این تقفاوت که از XListParam بجای XISetParam استفاده شده است.

 

3.5.3 کد user_xlist.cpp

#include "user_xlist.hpp"

 

const string UserRole::typeString[MAX] = {

"administrator",

"remote"

};

 

User *UserRole::newT() throw (Exception)

{

if (role.get_value() == ADMINISTRATOR)

return new Administrator;

if (role.get_value() == REMOTE)

return new RemoteUser;

 

throw Exception("Undefined user", TracePoint("user"));

}

 

int main()

{

Administrator administrator;

RemoteUser *remoteUser;

UserList userList;

UserList::ListIterator listIterator;

 

try {

userList.loadXmlDoc("user_list.xml");

administrator.set_username("root");

administrator.set_password("root");

userList.addT(administrator);

} catch (Exception &exception) {

cout << exception.what() << endl;

}

for (listIterator = userList.begin(); listIterator != userList.end();

++listIterator) {

remoteUser = dynamic_cast<RemoteUser*>(*listIterator);

if (remoteUser)

cout <<"user: "

<< remoteUser->get_username() << endl;

}

 

return 0;

}

تابع loadXmlDoc ساختار XML موجود در فایل ورودی را در کلاس بارگذاری می کند.