กันยายน 25, 2018, 07:12:21 am *
ยินดีต้อนรับคุณ, บุคคลทั่วไป กรุณา เข้าสู่ระบบ หรือ ลงทะเบียน
ส่งอีเมล์ยืนยันการใช้งาน?

เข้าสู่ระบบด้วยชื่อผู้ใช้ รหัสผ่าน และระยะเวลาในเซสชั่น
   หน้าแรก   ช่วยเหลือ เข้าสู่ระบบ สมัครสมาชิก  
หน้า: [1]   ลงล่าง
  พิมพ์  
ผู้เขียน หัวข้อ: C++ DLL and C-Warpper for LabVIEW  (อ่าน 1623 ครั้ง)
0 สมาชิก และ 1 บุคคลทั่วไป กำลังดูหัวข้อนี้
ShadowMan
Administrator
Hero Member
*****
ออฟไลน์ ออฟไลน์

เพศ: ชาย
กระทู้: 8272


ShadowWares


| |
« เมื่อ: เมษายน 15, 2015, 01:27:45 pm »

วันนี้จำเป็นต้องเขียน DLL เพื่อนำไปใช้ใน LabVIEW (Device Driver) โดยส่วนตัวชอบเขียน DLL หรือ Library อื่นๆ ด้วย Native-C เพราะสามารถย้าย Platform ได้โดยไม่ต้องแก้ code แต่บ่อยครั้งการเขียนด้วย C อาจจะใช้เวลามากกว่า C++ โดยเฉพาะอย่างยิ่งโปรแกรมที่ซับซ้อน และต้องการความสามารถเชิง OOP มีความจำเป็นต้องใช้ C++ แทนที่จะเป็น C (ใช้ C ได้ แต่ต้องออกแบบโปแกรมกันหนักหน่อย)
หัวข้อนี้จะขอแนะนำแนวทางหนึ่งสำหรับการเขียน C++ และ Compile ไปใช้กับ LabVIEW

1) ตัวอย่างของ C++ มีรูปแบบดังนี้

Code: (c)
class MyClass
{
private:
public:
   double *pMem;
   int id;
   unsigned int addr;
   int add(int a, int b);
   int sub(int a, int b);
   MyClass();
   ~MyClass();
};

MyClass::MyClass()
{
   this->pMem = new double[2048];
   for (int ii = 0; ii < 2048; ii++)
      this->pMem[ii] = 0.0;
   //MessageBox(0, "MyClass()", "MyClass()", MB_OK);
}

MyClass::~MyClass()
{
   delete this->pMem;
   this->pMem = NULL;
   //MessageBox(0, "~MyClass()", "~MyClass()", MB_OK);
}

int MyClass::add(int a, int b)
{
   return(a + b);
}

int MyClass::sub(int a, int b)
{
   return(a - b);
}

จากโปรแกรมในส่วนของ Constructor ได้ทำการของ memory ชนิด double ขนาก 2kB และเขียนค่า 0.0 ลงไปใน memory ส่วนนั้น
ในส่วนของ Destructor ไม่มีอะไรมากไปกว่าการคืนหน่วยความจำ
การกระทำในส่วนของ Constructor และ Destructor เพียงเพื่อทดสอบว่าหน่วยความจำไม่ไม่เกิดการ Leak เท่านั้น
สำหรับ function add() และ sub() เป็น member function ของ MyClass การทำงานของทั้งสอง function นี้คือบวกและลบตามลำดับ
และนั่นคือทั้งหมดในส่วนของ C++


โชคไม่ดี ที่เราไม่สามารถ export DLL ที่เขียนจาก C++ ไปใช้ใน LabVIEW ได้โดยตรง เพราะ LabVIEW ไม่ได้ถูกออกแบบมาให้ทำงานร่วมกับ C++ และจริงๆ ในรายละเอียดเชิงลึก C++ ไม่ได้ถูกออกแบบมาให้ export ได้นั่นเอง (ขอละรายละเอียดในส่วนนี้) จึงมีความจำเป็นต้องเขียน C มาห่อ C++ อีกที (C Wrapper) ความหมายคือ ใช้ C เรียก code ของ C++ นั่นเอง
คราวนี้มาดูกันว่าจะเขียน Wrapper ได้อย่างไร


เริ่มจากการ define และ ประกาศตัวแปร
Code: (c)
#define MAX_NOBJ 10
MyClass *ppObj[MAX_NOBJ];
int idx = -1;


MAX_NOBJ เป็นตัวกำกหนดจำนวน Object ของ C++
การกำหนดโดยวิธีนี้จำเป็นต้องตรวจสอบจำนวน Object ด้วย แต่ในที่นี้ เพื่อให้ง่ายต่อการอ่าน code จะละส่วนนี้ไป และเจะสมมุติว่า User ไม่มีทางสร้าง Object มากกว่า 10 ตัว (การสร้าง Object ที่ดีควรสร้างและเก็บใน Linked-List เพื่อความเป็น Dynamic Object-Oriented Programmingที่สมบูรณ์แบบ)

ppObj เป็น Array of Pointer to MyClass ใช้ เก็บ Object
idx เป็น Object Index ใช้ระบุตำแหน่งของ Object ใน Array ppObj

ต่อไปคือการประกาศฟังก์ชั่นต่างๆ สำหรับการ export รายละเอียดตามนี้:
Code: (c)

#define DLLEXPORT __declspec (dllexport)
#ifdef __cplusplus
extern "C" {
#endif
   DLLEXPORT int c_add(unsigned int p, int a, int b);
   DLLEXPORT int c_sub(unsigned int p, int a, int b);
   DLLEXPORT unsigned int c_init(void);
   DLLEXPORT void c_exit(unsigned int p);
#ifdef __cplusplus
}
#endif

ไม่มีอะไรแตกต่างจากการประกาศรูปแบบของฟังก์ชั่นเพื่อ export เป็น Library นั่นเอง

ต่อไปคือรายละเอียดของ Wrapper ฟังก์ชั่นต่างๆ:
Code: (c)


unsigned int c_init(void)
{
   MyClass *p = new MyClass();
   idx = (idx + 1) % MAX_NOBJ;
   p->id = idx;
   p->addr = (unsigned int)p;
   ppObj[idx] = p;
   return((unsigned int)p);
}

void c_exit(unsigned int p)
{
   delete (MyClass *)p; //can be used -> free((void *)p);
}

int c_add(unsigned int p, int a, int b)
{
   return(((MyClass *)p)->add(a, b));
}

int c_sub(unsigned int p, int a, int b)
{
   return(((MyClass *)p)->sub(a, b));
}


c_init() จะทำการสร้าง Object ของ Class และทำการคืนค่า address ของ Object ที่ถูกสร้างกลับไป
การกระทำในฟังก์ชั่นนี้ Constructor จะถูกเรียกอัตโนมัติ (โดยธรรมชาติของ C++) นั่นความความว่าจะมี memory ขนาด 2kB ถูกขอมาด้วย

c_exit() จะทำการลบ Object ที่อ้างอิงผ่านมาทาง input argument ของฟังก์ชั่น (address ของ object) ออกไปจากหน่วยความจำ
และการกระทำในฟังก์ชั่นนี้ Destructor จะถูกเรียกอัตโนมัติ (โดยธรรมชาติของ C++) นั่นความความว่าจะมี memory ขนาด 2kB ที่ถูกขอมาก่อนหน้าจะถูกคืนไปให้ระบบด้วย

c_add() และ c_sub() ทำการเรียกใช้ความสามารถของ Object add() และ sub() ตามลำดับ

** สังเกตุว่า ผมไม่มีการคืนค่า หรือรับค่าเป็น Pointer เพราะไม่มีความจำเป็นใดๆ เนื่องจากเราสามารำกำหนดให้ LabVIEW มอง Type *func(Type *p) เป็น Type func(Type p) นั่นเอง (การกำหนดรูปแบบการเรียก DLL ใน LabVIEW เป็นเรื่องละเอียดอ่อน) แต่อย่างไรก็ตาม ใน C เจ้า pointer ก็ยังคงเป็น pointer (หรือเป็นแค่เพียง address ของ memory ใดๆ) การเข้าถึงมันจะต้องเป็นไปตามรูปแบบที่ถูกต้อง ในที่นี้จะเห็นว่าผม cast ระหว่าง *p และ p ไปๆ มาๆ นั่งเอง **

และนั่นคือทั้งหมดของ C Wrapper ที่พร้อมใช้ใน LabVIEW
บันทึกการเข้า

By SDW: Do No Wrong Is Do Nothing
          If you want to increase your success rate, double your failure rate
ShadowMan
Administrator
Hero Member
*****
ออฟไลน์ ออฟไลน์

เพศ: ชาย
กระทู้: 8272


ShadowWares


| |
« ตอบ #1 เมื่อ: เมษายน 15, 2015, 04:28:50 pm »

ต่อไปมาดูวิธีที่เหมาะกับ C++ Programmer กันบ้าง

เปลี่ยนรูปแบบการประกาศฟังก์ชั่น Wrapper ใหม่ดังนี้:

Code: (c)
extern "C" {
#endif
#define DLLEXPORT __declspec (dllexport)

typedef struct MyClass MyClass;  

DLLEXPORT MyClass * x_init(void);
DLLEXPORT int x_add(MyClass * p, int a, int b);
DLLEXPORT int x_sub(MyClass * p, int a, int b);
DLLEXPORT void x_exit(MyClass * p);

#ifdef __cplusplus
}
#endif


รายละเดียดของ Wrapper:
Code: (c)

MyClass * x_init(void)
{
   return(new MyClass());
}

int x_add(MyClass * p, int a, int b)
{
   return(p->add(a, b));
}

int x_sub(MyClass * p, int a, int b)
{
   return(p->sub(a, b));
}

void x_exit(MyClass * p)
{
   delete p;
}

จะเห็นว่า code ดูเรียบง่ายกว่า (ในมุมมอง C++) และมีความเป็น Dynamic มากกว่าแบบก่อนหน้า
อย่างไรก็ตามในกรณีนี้ Pointer * ไม่มีความจำเป็นใดๆ นั่นหมายความว่าทั้ง 3 กรณีต่อไปนี้เหมือนกัน (LabVIEW และ Processor ใดๆ เห็น Pointer เป็น Address ตัวนึงเท่านั้น ซึ่ง Address คือตัวเลขจำนวณเต็มตัวนึงเท่านั้น):
Code: (c)
void x_exit(MyClass * p)
{
   delete p;
}

void x_exit2(WORD *p)
{
   delete (MyClass *)p;
}

void x_exit3(WORD p)
{
   delete (MyClass *)p;
}
บันทึกการเข้า

By SDW: Do No Wrong Is Do Nothing
          If you want to increase your success rate, double your failure rate
หน้า: [1]   ขึ้นบน
  พิมพ์  
 
กระโดดไป: