物件再生

物件再生是結合永續儲存機動化元件的進階架構,可以從永續儲存的資料完整重生一個物件,一個可重生的元件須是 class cxxlPreserve2 的延伸類別,且和機動化元件一樣須放在元件檔(dll 檔)中。

 class cxxlPreserve2 繼承自 class cxxlPreserve,其中加入以下五個重要屬性:

  __InterfaceID 用來尋找 dll 的 UUID
  __ClassID 實作 class 的 UUID
  __ObjID 物件被產生時 CentralizedMgr 會賦予一個 UUID
  __Group 指示 __InterfaceID 要在元件註冊庫中哪個 Group 中尋找 dll
         __Spirit 產生物件時所搭配的 ISpirit
 

以上這些屬性主要是儲存於永續儲存資料中,以便再生時使用。除了 ClassID 是由各層 class 的建構子指定外,其他都在 cxxlCM_GetElement() 成功取得物件時由 CentralizedMgr 設定。

設計原則如下:

  1. 要遵守 cxxlPreserve 設計原則和機動化元件的建構方法,必須經由 cxxlCM_GetElement() 來產生物件,否則不具有永存和再生的能力。
  2. 從 cxxlPreserve2 繼承的介面都 必須用 virtual 方式繼承 cxxlPreserve2,每層 cxxlPreserve2 的延伸類別都須在建構時指定 cxxlPreserve2 的 ClassID,一般都是用 UUID。
  3. 須為介面的 ClassID(即 __InterfaceID) 做元件註冊,並用 Group 參數分別不同的實作版本。
  4. 子物件若是 cxxlPreserve 要用 PSmart_Ptr 或 PSmart_Sst 持有,若是 cxxlPreserve2 須改用 P2Smart_Ptr 或 P2Smart_Set,P2Smart 連結的子類別和父類別都須是 cxxlPreserve2。
  5. 須把 IcxxlStorage 和 Smart_Ptr<T> (T 為當前的類別名稱) 加入介面類別的 friend 名單。

元件檔除了要和機動化元件一樣提供 CxxlMan_ObjectFactory() 外,還要再提供一個 CxxlMan_Preserve2Factory(),供再生時叫用,原型宣如下:

extern "C" CXXL_DLLEXPORT
 cxxlPreserve2 * cxxlCDECL CxxlMan_Preserve2Factory(const wchar_t *ClassID)

和 CxxlMan_ObjectFactory() 做比較,除了參數比較少回傳改為 cxxlPreserve2 外,最重要的是 CxxlMan_ObjectFactory() 的 ClassID 參數是元件註冊時所指定的 ClassID(即 __InterfaceID),而 CxxlMan_Preserve2Factory 的 ClassID 是實作 class 的 ClassID(即 __ClassID)。

另外 CxxlMan_ObjectFactory() 也須加入實作 class 的 friend 名單中。

設計 Preserve2 元件

Preserve2 是融合了永續儲存機動化元件的設計,須做成 dll 動態連結形式的元件檔,首先設計好要用的介面,這個範例元件可以用來儲存多筆名字和年齡兩項資料,也可以顯示指定名稱的資料,和把所有的資料都列示出來

 

範例一

/***********************
** NameDirectory.h     
***********************/
#ifndef NAMEDIRECTORY_H_INCLUDED
#define NAMEDIRECTORY_H_INCLUDED
#include <PRESERVE2.HPP>
// INameList 元件介面的識別碼
#define INameList_ClassID L"888C0D07-2CB7-4561-AF73-6B72D29EA2FB"
// 元件介面
class INameList:virtual public CxxlMan::cxxlPreserve2  
{

protected:
  // 儲存容器藉此取得要永續存取的屬性資料
  // 延伸類別須以 virtual 的形式覆寫
  // 並且須呼叫上層類別的此函數
  virtual bool cxxlFASTCALL Ref(CxxlMan::Filter *F)
  {
    if(F != NULL && cxxlPreserve2::Ref(F))
      return true;
    else
      return false;
  }
public:
  // Constructor
  INameList()
    :cxxlObject(CxxlMan::Spirit_Easy),
CxxlMan::cxxlPreserve2(INameList_ClassID) // 在建構時指定 ClassID
{ } // Destructor virtual ~INameList() { }
  virtual void Input(const CxxlMan::UNICODE_String &Name_Arg, int Age_Arg) = 0; // 輸入
  virtual void Show(const CxxlMan::UNICODE_String &Name_Arg) = 0;  // 顯示所指名稱的資料
  virtual void ShowAll() = 0; // 顯示所有資料
  friend class CxxlMan::IcxxlStorage;          // Preserve2 元件須加上這個
  friend class CxxlMan::Smart_Ptr<INameList>;  // Preserve2 元件須加上這個
};
#endif // NAMEDIRECTORY_H_INCLUDED
 

以上 Preserve2 的做法和 Preserve 相似,只多了一些 Preserve2 特有的東西,接下來是實作


範例二

/***********************
** NameDirectory.cpp
***********************/
#include <sstream>
#include <iostream>
#include <CXXLCOMPOSITE.HPP>
#include <CXXLLIST.HPP>
#include <CENTRALIZEDMGR.HPP>
#include "NameDirectory.h"
using namespace std;
using namespace CxxlMan;
// cxxlPreserve2 元件的識別碼,沒對外公佈
#define CNameList_ClassID L"C458BFCF-BB67-4e2e-91C6-43FBFE6EA8C3"
#define DataBase_ClassID L"0C494B0B-D168-480c-97BD-6C6E8D4BD165"
extern "C" CXXL_DLLEXPORT
cxxlObject * cxxlCDECL CxxlMan_ObjectFactory(const wchar_t *ClassID, 
void *Arg, ISpirit *spirit);
struct Data:public cxxlObject
{
  UNICODE_String Name;
  int Age;
  Data(const UNICODE_String &Name_Arg, int Age_Arg)
    :cxxlObject(Spirit_Urgent),
    Name(6)       // 固定 5 個字元加一個結尾字元共 6 個字元空間
  {
    int StrLength = Name_Arg.StrLength();
    if(StrLength > 5)
    {
      for(int i = 0; i < 6; ++i)
        Name[i] = Name_Arg[i];
        Name[5] = L'\0';
    }
    else
      Name = Name_Arg;
    Age = Age_Arg;
  }
};
 
// 雖然在這沒必要,為了示範物件連結仍從 cxxlPreserve2 延伸
class DataBase:virtual public cxxlPreserve2
{
  Smart_Ptr<cxxlComposite<Data, false> > Data_Composite;    // 實際存放的地方
  Smart_Ptr<cxxlComposite<Data, false> > Data_Composite_bp; // 永存處理時的暫時存放區
  // 儲存容器藉此取得要永續存取的屬性資料
  // 延伸類別須以 virtual 的形式覆寫
  // 並且須呼叫上層類別的此函數
  virtual bool cxxlFASTCALL Ref(Filter *F)
  {
    if(F != NULL && cxxlPreserve2::Ref(F))
    {
      LoadFilter *pLoadFilter = dynamic_cast<LoadFilter*>(F);
      if(pLoadFilter == NULL) // 表示 F 是 SaveFilter
      {
        Smart_Ptr<cxxlList<cxxlComposite<Data, false> > > DataComposite_List = 
Data_Composite->cxxlList_Create(); unsigned long Count = DataComposite_List->GetCount(); F->Ref(&Count, 1, L"DataCount");
        DataComposite_List->ResetPT(toHead);
        Smart_Ptr<cxxlComposite<Data, false> > DataComposite_Ptr = (*DataComposite_List)++;
        for(unsigned long i = 0;
          i < Count;
          ++i, DataComposite_Ptr = (*DataComposite_List)++)
        {
          Smart_Ptr<Data> Data_Ptr = DataComposite_Ptr->GetObj();
          F->Ref((wchar_t*)(const wchar_t*)Data_Ptr->Name, 
Data_Ptr->Name.StrLength()+1, L"Name"); F->Ref(&Data_Ptr->Age, 1, L"Age"); } } else // F 是 LoadFilter { if(pLoadFilter->isChk()) { unsigned long *DataCount; size_t Count;
          if(pLoadFilter->LoadChk((const long **)&DataCount, &Count, L"DataCount") == false)
            return false;
          Data_Composite_bp = new cxxlComposite<Data, false>(L"Roll",Spirit_Urgent);
          for(unsigned long i = 0; i < *DataCount; ++i)
          {
            const wchar_t *data_name;
            const int *data_age;
            if(pLoadFilter->LoadChk(&data_name,&Count,L"Name") == false)
              return false;
            if(pLoadFilter->LoadChk(&data_age,&Count,L"Age") == false)
              return false;
            Smart_Ptr<Data> Data_Ptr(new Data(L"",0));
            Data_Composite_bp->Add(Data_Ptr,data_name);
          }
        }
        else // 實際讀取階段
        {
          unsigned long Count;
          F->Ref(&Count,1, L"DataCount");
          Smart_Ptr<cxxlList<cxxlComposite<Data, false> > > DataComposite_List = 
Data_Composite_bp->cxxlList_Create(); DataComposite_List->ResetPT(toHead); Smart_Ptr<cxxlComposite<Data, false> >
DataComposite_Ptr = (*DataComposite_List)++; for(unsigned long i = 0; i < Count; ++i,
DataComposite_Ptr = (*DataComposite_List)++) { Smart_Ptr<Data> Data_Ptr = DataComposite_Ptr->GetObj(); F->Ref((wchar_t*)(const wchar_t*)Data_Ptr->Name, 0, (wchar_t*)NULL); F->Ref(&(Data_Ptr->Age), 0, (wchar_t*)NULL); }
          Data_Composite = Data_Composite_bp;
        }
      }
      return true;
    }
    else

      return false;
  }
public:
  // Constructor
  DataBase()
    :cxxlObject(Spirit_Urgent),
cxxlPreserve2(DataBase_ClassID), // 在建構時指定 ClassID
Data_Composite(new cxxlComposite<Data, false>(L"Roll",Spirit_Urgent), this), Data_Composite_bp(NULL, this) { } // Destructor virtual ~DataBase() { }
  void Add(const Smart_Ptr<Data> &Data_Arg, const UNICODE_String &Name_Arg)
  {
    Data_Composite->Add(Data_Arg, Name_Arg);
  }
  Smart_Ptr<Data> GerData(const UNICODE_String &Name_Arg)
  {
    return Data_Composite->GetObj(Name_Arg);
  }
  Smart_Ptr<cxxlList<Data> > GerAllData()
  {
    Smart_Ptr<cxxlList<cxxlComposite<Data, false> > > 
DataComposite_List = Data_Composite->cxxlList_Create();
    Smart_Ptr<cxxlList<Data> > Data_List = new cxxlList<Data>();
    DataComposite_List->ResetPT(toHead);
    for(Smart_Ptr<cxxlComposite<Data, false> > DataComposite_Ptr = (*DataComposite_List)++;
      DataComposite_Ptr.isNULL() == false;
      DataComposite_Ptr = (*DataComposite_List)++)
    {
      Data_List->Push_Back(DataComposite_Ptr->GetObj());
    }
    return Data_List;
  }
  // 實作的 Preserve2 元件還要再加上這個
  friend cxxlObject * cxxlCDECL 
CxxlMan_ObjectFactory(const wchar_t *ClassID, void *Arg, ISpirit *spirit);
};
// INameList 的實作
class CNameList:public INameList
{
  P2Smart_Ptr<DataBase> DataBase_Ptr;
  // 指示再生時到哪尋找 dll 元件檔
virtual UNICODE_String cxxlFASTCALL GetGroup()
{
return UNICODE_String(L"testGroup");
}
  // 儲存容器藉此取得要永續存取的屬性資料
  // 延伸類別須以 virtual 的形式覆寫
  // 並且須呼叫上層類別的此函數
  virtual bool cxxlFASTCALL Ref(Filter *F)
  {
    if(F != NULL && INameList::Ref(F))
      return true;
    else
      return false;
  }
public:
  // Constructor 新建立時用的
  CNameList(ISpirit *spirit)
    :cxxlObject(spirit),
cxxlPreserve2(CNameList_ClassID), // 在建構時指定 ClassID
// 這裡其實是可以不須採用 Spirit_Urgent,會用 Spirit_Urgent 是為了下面的使用範例要求的即時性 DataBase_Ptr(Preserve2_Cast<DataBase>( cxxlCM_GetElement(DataBase_ClassID,
L"testGroup", NULL, Spirit_Urgent) )
, this, L"DataBase") { }
  // Constructor 再生時用的
  CNameList()
    :cxxlObject(Spirit_Easy), // 再生時 CentralizedMgr 會用永存資料中記錄的 Spirit 換掉
cxxlPreserve2(CNameList_ClassID), // 在建構時指定 ClassID
DataBase_Ptr(NULL, this, L"DataBase")//再生時會以永存資料中記錄的元件資訊產生一個物件填補上去 { }
  // Destructor
  virtual ~CNameList()
  {
  }
  virtual void Input(const UNICODE_String &Name_Arg, int Age_Arg) // 輸入
  {
    DataBase_Ptr->Add(new Data(Name_Arg, Age_Arg), Name_Arg);
  }
  virtual void Show(const UNICODE_String &Name_Arg)  // 顯示所指名稱的資料
  {
    Smart_Ptr<Data> Data_Ptr = DataBase_Ptr->GerData(Name_Arg);
    wcout << L"名稱:" << (const wchar_t*)Data_Ptr->Name << L" "
          << L"年紀:" << Data_Ptr->Age << endl;
  }
  virtual void ShowAll() // 顯示所有資料
  {
    Smart_Ptr<cxxlList<Data> > Data_List = DataBase_Ptr->GerAllData();
    Data_List->ResetPT(toHead);
    for(Smart_Ptr<Data> Data_Ptr = (*Data_List)++;
      Data_Ptr.isNULL() == false;
      Data_Ptr = (*Data_List)++)
    {
      wcout << L"名稱:" << (const wchar_t*)Data_Ptr->Name << L" "
            << L"年紀:" << Data_Ptr->Age << endl;
    }
  }
  // 實作的 Preserve2 元件還要再加上這個
  friend cxxlObject * cxxlCDECL CxxlMan_ObjectFactory(const wchar_t *ClassID, 
void *Arg, ISpirit *spirit);
};
// 負責註冊沒對外公佈的元件識別碼,
class Init
{
  static void Reg()
  {
    wstring RegTbl(
       L"[testGroup]"
       L"{"
       L"  [0C494B0B-D168-480c-97BD-6C6E8D4BD165] = \"SampleComponent.dll\""
       L"}");
    // 匯入註冊表
    wistringstream Wins( RegTbl );
    cxxlCM_ElementImport(Wins);
  }

public:
  Init()
  {
    Reg();
  }
};
extern "C" CXXL_DLLEXPORT
cxxlObject * cxxlCDECL CxxlMan_ObjectFactory(const wchar_t *ClassID, void *Arg, 
ISpirit *spirit) { static Init init;
  if(wcscmp(INameList_ClassID, ClassID) == 0)  // 只處理介面就好
    return new CNameList(spirit);
/*
if(wcscmp(CNameList_ClassID, ClassID) == 0) // 不應處理這個,因 CNameList_ClassID 不
return new CNameList(spirit); // 是用要用於 cxxlCM_GetElement()
*/
  if(wcscmp(DataBase_ClassID, ClassID) == 0)
    return new DataBase;
  return NULL;
}
extern "C" CXXL_DLLEXPORT
cxxlPreserve2 * cxxlCDECL CxxlMan_Preserve2Factory(const wchar_t *ClassID)
{
  static Init init;
  if(wcscmp(CNameList_ClassID, ClassID) == 0) // 只處理最終實作類別的識別碼就行了
     return new CNameList();
  if(wcscmp(DataBase_ClassID, ClassID) == 0)
    return new DataBase;
  return NULL;
}
 
 

範例二的實作有點長,DataBase::Ref() 看起來有些繁雜,因要永存的資料筆數不定,沒法用簡易的方法,不過那不是這裡要討論的重點,可以不用去理會。

DataBase_ClassID 這個識別碼是要用於 cxxlCM_GetElement(),但沒對外公佈的,因此使用端沒法替它們註冊,因此元件檔須自行註冊,正確的做法應在 dll 載入和卸載時註冊和註銷,比如寫在 Windows平台 的 DllMain() 之中,這裡為了簡便就直接在這用 init 在 CxxlMan_ObjectFactory() 和 CxxlMan_Preserve2Factory() 註冊,註冊兩次以上並不會發生問題。另外在多執行緒下會不會造成 static Init init 發生 memory leak,不會的,因 CxxlMan_ObjectFactory() 和 CxxlMan_Preserve2Factory() 是由物件管理中心呼叫,而物件管理中心一次只服務一個執行緒。

元件設計無誤編譯完成後,就可以把元件檔(SampleComponent.dll)和引入檔(NameDirectory.h )發佈出去

 

 

使用範例

範例三

#include <iostream>
#include <locale>
#include <CXXLLIST.HPP>
#include <CENTRALIZEDMGR.HPP>
#include <NameDirectory.h>
using namespace std;
using namespace CxxlMan;
// 產生一個註冊表
wstring CreateRegTbl()
{
  wstring RegTbl(
    L"[SampleComponent v1.0]"
    L"{"
    L"  [888C0D07-2CB7-4561-AF73-6B72D29EA2FB] = \"SampleComponent.dll\""
    L"}");
  return RegTbl;
}
// 使用 INameList 元件來工作的類別
class User:public cxxlObject
{
  Smart_Ptr<INameList> INameList_Ptr;
public:
  // Constructor
  User(const Smart_Ptr<INameList> &INameList_Arg)
    :INameList_Ptr(INameList_Arg)
  {
  }
  // Destructor
  virtual ~User()
  {
  }
  void Input()
  {
    wstring Name;
    int Age;
    cout << "請輸入姓名(最多 5 個字元):"; wcin >> Name;
    cout << "請輸入年齡:"; cin >> Age;

    INameList_Ptr->Input(Name.c_str(),Age);
    cout << "作業完成..." << endl << endl;
  }
  void Show()
  {
    wstring Name;
    cout << "請輸入姓名(最多 5 個字元):"; wcin >> Name;
    INameList_Ptr->Show(Name.c_str());
    cout << "作業完成..." << endl << endl;
  }
  void ShowAll()
  {
    INameList_Ptr->ShowAll();
    cout << "作業完成..." << endl << endl;
  }
};
#define StrPkg string_wrapper<wchar_t>
int main(int argc, char* argv[])
{
  setlocale(LC_CTYPE, "");            // 指定系統現正使用中的地區語言做轉換
  // 以主程式(exe 檔)所在路徑為搜尋路徑,因元件檔(dll 檔)置於同一資料夾中
  UNICODE_String BasePath(argv[0]);
  *(wchar_t*)wcsrchr((const wchar_t*)BasePath,L'\\') = L'\0';
  Smart_Ptr<cxxlList<StrPkg > > BasePath_List(new cxxlList<StrPkg >);
  BasePath_List->Push_Back(BasePath);
  cxxlCM_Init(BasePath_List);
  // 匯入註冊表
  wistringstream Wins( CreateRegTbl() );
  cxxlCM_ElementImport(Wins);
  // 取得物件,注意須用 Preserve2_Cast 轉型
  Smart_Ptr<INameList> INameList_Ptr = 
Preserve2_Cast<INameList>( cxxlCM_GetElement(INameList_ClassID,
L"SampleComponent v1.0") );
  Smart_Ptr<User> User_Ptr(new User(INameList_Ptr));
  bool isQuit = false;
  while(isQuit == false)
  {
    cout << "請選擇 1)輸入  2)顯示指定的資料  3)顯示所有資料  0)結束:";
    int Choose;
      cin >> Choose;
    switch(Choose)
    {
    case 1:
      User_Ptr->Input();
      break;
    case 2:
      User_Ptr->Show();
      break;
    case 3:
      User_Ptr->ShowAll();
      break;
    case 0:
      isQuit = true;
      break;
    default:
      cout << "選擇錯誤..." << endl;
    }
  }
  return 0;
}

 以上的範例只是一般元件的用法,沒什麼特別,接下來展示 Preserve2 特殊功能。

 

範例四

#include <iostream>
#include <locale>
#include <CXXLLIST.HPP>
#include <CENTRALIZEDMGR.HPP>
#include <TXTSTORAGE.HPP>
#include "NameDirectory.h"

using namespace std;
using namespace CxxlMan;
// 產生一個註冊表
wstring CreateRegTbl()
{
  wstring RegTbl(
     L"[SampleComponent v1.0]"
     L"{"
     L"  [888C0D07-2CB7-4561-AF73-6B72D29EA2FB] = \"SampleComponent.dll\""
     L"}");
  return RegTbl;
}
 
#define StrPkg string_wrapper<wchar_t>
int main(int argc, char* argv[])
{
  setlocale(LC_CTYPE, "");            // 指定系統現正使用中的地區語言做轉換
  // 以主程式(exe 檔)所在路徑為搜尋路徑,因元件檔(dll 檔)置於同一資料夾中
  UNICODE_String BasePath(argv[0]);
  *(wchar_t*)wcsrchr((const wchar_t*)BasePath,L'\\') = L'\0';
  Smart_Ptr<cxxlList<StrPkg > > BasePath_List(new cxxlList<StrPkg >);
  BasePath_List->Push_Back(BasePath);
  cxxlCM_Init(BasePath_List);
  // 匯入註冊表
  wistringstream Wins( CreateRegTbl() );
  cxxlCM_ElementImport(Wins);
  // 取得物件,注意須用 Preserve2_Cast 轉型
  Smart_Ptr<INameList> INameList1_Ptr = 
Preserve2_Cast<INameList>( cxxlCM_GetElement(INameList_ClassID,
L"SampleComponent v1.0", NULL,
Spirit_Urgent) );
  // 任意輸入幾筆資料
  INameList1_Ptr->Input(L"王小明", 13);
  INameList1_Ptr->Input(L"李志遠", 15);
  INameList1_Ptr->Input(L"蔡秋霞", 10);
  wcout << L"INameList1_Ptr 內含的所有資料..." << endl;
  INameList1_Ptr->ShowAll();
  wcout << L"--------------------------------------" << endl;
  // 把 Storage1_Ptr 永續資料放入 Storage1_Ptr 中
  Smart_Ptr<IcxxlStorage> Storage1_Ptr(new TxtStorage);
  Storage1_Ptr->Save(INameList1_Ptr,L"INameList_Backup");
  // 由儲存體產生一個新物件
  Smart_Ptr<cxxlPreserve2> P2tmp_Ptr(NULL);
  REGENERARETURN_TYPE r = cxxlCM_Regenera(P2tmp_Ptr, L"INameList_Backup", 
Storage1_Ptr, false); if(r != Regenera_Success) { wcout << "再生失敗" << endl; exit(0); } Smart_Ptr<INameList> INameList2_Ptr = Smart_Cast<INameList>(P2tmp_Ptr);
  // INameList1_Ptr 新增一筆資料
  INameList1_Ptr->Input(L"孫小美", 20);
  wcout << endl << L"在 INameList1_Ptr 新增一筆資料: 孫小美, 20" << endl
        << L"看看 INameList2_Ptr 的內容..." << endl;
  INameList2_Ptr->ShowAll();
  wcout << L"--------------------------------------" << endl;
  // 清光持有者
  INameList1_Ptr.Destroy();
  INameList2_Ptr.Destroy();
  P2tmp_Ptr.Destroy();
  cxxlCM_Regenera(P2tmp_Ptr, L"INameList_Backup", Storage1_Ptr, false);
  Smart_Ptr<INameList> INameList3_Ptr = Smart_Cast<INameList>(P2tmp_Ptr);
  wcout << endl << L"清光持有者之後,再生一個 INameList3_Ptr" << endl
        << L"看看 INameList3_Ptr 的內容..." << endl;
  INameList3_Ptr->ShowAll();
  wcout << L"--------------------------------------" << endl;
  // 清光持有者
  INameList1_Ptr.Destroy();
  INameList2_Ptr.Destroy();
  INameList3_Ptr.Destroy();
  P2tmp_Ptr.Destroy();
  cxxlCM_Regenera(P2tmp_Ptr, L"INameList_Backup", Storage1_Ptr, true);
  Smart_Ptr<INameList> INameList4_Ptr = Smart_Cast<INameList>(P2tmp_Ptr);
  wcout << endl << L"清光持有者之後,再生一個 INameList4_Ptr,這次將 isRestore 參數設為 true" 
<< endl << L"看看 INameList4_Ptr 的內容..." << endl; INameList4_Ptr->ShowAll(); wcout << L"--------------------------------------" << endl;
  wostringstream woss;
  TxtStorageExportTLC(woss, Smart_Cast<TxtStorage>(Storage1_Ptr));
  wcout << endl << L"將 Storage1_Ptr 儲存的永存資枓列出如下..." << endl;
  wcout << woss.str() << endl;
  return 0;
}

 

執行結果的截圖:

  

 範例四在展示再生的效應,先注意一下 INameList1_Ptr 取得的物件是採用 Spirit_Urgent 即時處理的模式。再生函數 cxxlCM_Regenera() 的回傳值 REGENERARETURN_TYPE 是執行的結果回報,其意義請參閱 PRESERVECOMMON.HPP

INameList2_Ptr 雖然是由永存資料再生出來的,但可以看出和 INameList1_Ptr 持有的是同一個物件。當一個 Preserve2 元件經由物件管理中心產生(使用 cxxlCM_GetElement() 函數)時,會賦予一個 ObjID 值,這是一個由物件管理中心自行產生 UUID 的字串,把這物件用永緒儲存的方式儲存時,也會一併儲存這個 ObjID 值。

當由這個永存資料再生(使用 cxxlCM_Regenera 函數)時,會先看永存資料中的 ObjID 所指的物件是不是已經存在,若存在就直接用這個物件,否則就由永存資料中的 ClassID 產生一個新物件,但其 ObjID 則用永存資料中的 ObjID,同時會看 cxxlCM_Regenera 的 isRestore 參數若為 true,還會只為這新物件(不包含子物件)做永存資料的取回動作。現在可以瞭解 INameList2_Ptr 為什麼和 INameList1_Ptr 會持有同一個物件了吧。

INameList3_Ptr 是在先前所有持有者都執行放棄動作後,再生所得到的新物件,因未把 isRestore 參數設為 true,所以內容就空空的,還有那個所有持有者的放棄動作能發揮作用,是因物件建立時是採用 Spirit_Urgent,若採用 Spirit_Easy 只是把物件放入待刪佇列,執行再生時有可能物件仍然活著。

INameList4_Ptr 和 INameList3_Ptr 一樣是所有持有者都放棄後再生的,因有把 isRestore 參數設為 true,而資料為什麼只有三筆少了一筆,因永存資料是在輸入第 4 筆之前建立的,所以沒有第 4 筆資料。

 

範例五

#include <iostream>
#include <locale>
#include <CXXLLIST.HPP>
#include <CENTRALIZEDMGR.HPP>
#include <TXTSTORAGE.HPP>
#include "NameDirectory.h"
using namespace std;
using namespace CxxlMan;
// 產生一個註冊表
wstring CreateRegTbl()
{
  wstring RegTbl(
     L"[SampleComponent v1.0]"
     L"{"
     L"  [888C0D07-2CB7-4561-AF73-6B72D29EA2FB] = \"SampleComponent.dll\""
     L"}");
  return RegTbl;
}
 
#define StrPkg string_wrapper<wchar_t>
int main(int argc, char* argv[])
{
  setlocale(LC_CTYPE, "");            // 指定系統現正使用中的地區語言做轉換
  // 以主程式(exe 檔)所在路徑為搜尋路徑,因元件檔(dll 檔)置於同一資料夾中
  UNICODE_String BasePath(argv[0]);
  *(wchar_t*)wcsrchr((const wchar_t*)BasePath,L'\\') = L'\0';
  Smart_Ptr<cxxlList<StrPkg > > BasePath_List(new cxxlList<StrPkg >);
  BasePath_List->Push_Back(BasePath);
  cxxlCM_Init(BasePath_List);
  // 匯入註冊表
  wistringstream Wins( CreateRegTbl() );
  cxxlCM_ElementImport(Wins);
  // 取得物件,注意須用 Preserve2_Cast 轉型
  Smart_Ptr<INameList> INameList1_Ptr = 
Preserve2_Cast<INameList>( cxxlCM_GetElement(INameList_ClassID,
L"SampleComponent v1.0", NULL, Spirit_Urgent) );
  // 任意輸入幾筆資料
  INameList1_Ptr->Input(L"王小明", 13);
  INameList1_Ptr->Input(L"李志遠", 15);
  INameList1_Ptr->Input(L"蔡秋霞", 10);
  // 把 Storage1_Ptr 永續資料放入 Storage1_Ptr 中
  Smart_Ptr<IcxxlStorage> Storage1_Ptr(new TxtStorage);
  Storage1_Ptr->Save(INameList1_Ptr,L"INameList_Backup");
  // INameList1_Ptr 新增一筆資料
  INameList1_Ptr->Input(L"孫小美", 20);
 
  // 清除掉永存資料中的 ObjID 資訊
  if(cxxlCM_CutPreserve2(Storage1_Ptr,true,false) == false)
  {
    wcout << L"發現永存資料 ObjID 有循環參照的狀況" << endl;
    exit(0);
  }
  // 由儲存體產生一個新物件,並取回永存的資料
  Smart_Ptr<cxxlPreserve2> P2tmp_Ptr(NULL);
  cxxlCM_Regenera(P2tmp_Ptr, L"INameList_Backup", Storage1_Ptr, true);
  Smart_Ptr<INameList> INameList2_Ptr = Smart_Cast<INameList>(P2tmp_Ptr);
  wcout << L"INameList1_Ptr 內含的所有資料..." << endl;
  INameList1_Ptr->ShowAll();
  wcout << L"--------------------------------------" << endl;
  wcout << endl << L"在 INameList1_Ptr 新增一筆資料: 孫小美, 20" << endl
        << L"看看 INameList2_Ptr 的內容..." << endl;
  INameList2_Ptr->ShowAll();
  wcout << L"--------------------------------------" << endl;
wostringstream woss; TxtStorageExportTLC(woss, Smart_Cast<TxtStorage>(Storage1_Ptr)); wcout << endl << L"將 Storage1_Ptr 儲存的永存資枓列出如下..." << endl; wcout << woss.str() << endl;
  return 0;
}
 

 

執行結果的截圖:

 
 
 

範例五在介紹 cxxlCM_CutPreserve2() 函數的功用,原型宣告如下:

// 切斷 IcxxlStorage_Arg 中所有 cxxlPreserve2 永存資料的資訊
// ClearObjID 若為 true 即讓 ObjID 資訊失去作用,再生時不再是共用物件,會產生獨立的物件
// DelRestore 若為 true,除了 cxxlPreserve2 的永存資料外,其他永存資料全刪除,這樣只能做純粹再生
// 若 ClearObjID 為 true,有循環參照會回覆 false,回傳值只有 ClearObjID 為 true 才有意義
bool cxxlCM_CutPreserve2(const Smart_Ptr<IcxxlStorage> &IcxxlStorage_Arg, 
bool ClearObjID, bool DelRestore);
// 切斷 IcxxlStorage_Arg 中,指定名稱的 cxxlPreserve2 永存資料的資訊
// ClearObjID 若為 true 即讓 ObjID 資訊失去作用,再生時不再是共用物件,會產生獨立的物件
// DelRestore 若為 true,除了 cxxlPreserve2 的永存資料外,其他永存資料全刪除,這樣只能做純粹再生
// 若 ClearObjID 為 true,有循環參照會回覆 false,回傳值只有 ClearObjID 為 true 才有意義,注意指
// 定名稱的資料不存在仍回覆 true bool cxxlCM_CutPreserve2(const Smart_Ptr<IcxxlStorage> &IcxxlStorage_Arg,
const UNICODE_String &Name, bool ClearObjID, bool DelRestore);

若你想把永存資料中和其他物件的關聯性清掉,可把 ClearObjID 參數設為 true,這樣會把永存資料中所有的 ObjID 資料清掉,若你只想把其中一部份的 ObjID 清掉,可把永存資料存成文字檔(限使用 TxtStorage 儲存體),須存成 Uncode 碼,再用文字編輯軟體把 [__ObjID-Count] 這一項的值設為 "0"。 ClearObjID 參數只有對非循環參照的物件資料才會執行成功,所謂循環參照就是指 A 物件含有子物件 B,B 物件含有子物件 C,而 C 物件又含有子物件 A,這樣的物件結構是解不開的。

範例五中永存資料的 ObjID 已被清掉了,所以 INameList1_Ptr 和 INameList2_Ptr 所持有的是兩個獨立的物件

DelRestore 參數是要為永存資料瘦身用的,若你只是要拿來產生物件使用,可以用它把存放在裡面的一般資料清掉

  
 

增修後記

 r048 P2Smart_Set 內部所用的容器,由原先的 cxxlAvl_Tree 改為 SList,讓放入的順序即為排列的順序
 r050P2Smart_Set 增加 GetCount() 成員函數

 


引入檔

CENTRALIZEDMGR.HPP

程式庫

cxxlpreserve

 

 
 
 
ċ
本文範例.7z
(7k)
Cxxlman Cxxlman,
2012年7月5日 晚上7:23
Comments