Node.js لها أفضل تأثير في كتابة الخلفية باستخدام JavaScript ، ويستحق تجربة المزيد. ومع ذلك ، إذا كنت بحاجة إلى بعض الوظائف التي لا يمكن استخدامها مباشرة أو حتى وحدات لا يمكن تنفيذها على الإطلاق ، فهل يمكنك تقديم مثل هذه الإنجازات من مكتبة C/C ++؟ الجواب نعم. كل ما عليك فعله هو كتابة مكون إضافي واستخدام موارد قاعدة رمز أخرى في رمز JavaScript الخاص بك. لنبدأ رحلة استفسار اليوم معًا.
يقدم
كما يقول Node.js في الوثائق الرسمية ، فإن المكونات الإضافية هي كائنات مشتركة تربط ديناميكيًا ، والتي يمكنها توصيل رمز JavaScript بمكتبات C/C ++. هذا يعني أنه يمكننا الرجوع إلى أي شيء من مكتبة C/C ++ ودمجها في Node.js عن طريق إنشاء مكونات إضافية.
على سبيل المثال ، سنقوم بإنشاء تغليف لكائن Std :: String Standard.
تحضير
قبل أن نبدأ في الكتابة ، نحتاج إلى التأكد من قمنا بإعداد جميع المواد اللازمة لتجميع الوحدة اللاحقة. الجميع يحتاج إلى GYP وجميع التبعيات. يمكنك استخدام الأمر التالي لتثبيت Node-GYP:
تثبيت NPM -g Gyp -gyp
من حيث التبعيات ، نحتاج إلى إعداد المشاريع التالية لأنظمة UNIX: • Python (يتطلب الإصدار 2.7 ، 3.x لا يمكن العمل بشكل صحيح)
• يصنع
• مجموعة أدوات برنامج التحويل البرمجي C ++ (مثل GPP أو G ++)
على سبيل المثال ، في Ubuntu ، يمكنك استخدام الأمر التالي لتثبيت جميع المشاريع المذكورة أعلاه (يجب أن يكون Python 2.7 مثبتًا مسبقًا):
sudo apt-get instrant-issentials
في بيئة نظام Windows ، ما تحتاجه هو:
• Python (الإصدار 2.7.3 ، 3.x لا يمكن العمل بشكل طبيعي)
• Microsoft Visual Studio C ++ 2010 (لنظام التشغيل Windows XP/Vista)
• Microsoft Visual Studio C ++ 2012 لنظام التشغيل Windows Desktop (لنظام التشغيل Windows 7/8)
للتأكيد ، يمكن أن تعمل النسخة السريعة من Visual Studio بشكل طبيعي.
ملف binding.gyp
يتم استخدام هذا الملف بواسطة Node-GYP وهو مصمم لإنشاء ملف الإنشاء المناسب للمكون الإضافي الخاص بنا. يمكنك النقر هنا لعرض مستند وصف ملف .gyp الذي توفره ويكيبيديا ، ولكن المثال الذي نريد استخدامه اليوم بسيط للغاية ، لذلك تحتاج فقط إلى استخدام الرمز التالي:
{"الأهداف": [{"target_name": "stdstring" ، "المصادر": ["addon.cc" ، "stdstring.cc"]}]}حيث يمكن تعيين Target_Name على أي شيء تريده. تحتوي صفيف المصادر على جميع الملفات المصدر التي يحتاجها المكون الإضافي إلى استخدامها. في مثالنا ، يتم تضمين addon.cc أيضًا ، والذي يتم استخدامه لاستيعاب الكود اللازم لتجميع المكونات الإضافية و stdstring.cc ، بالإضافة إلى فئة التغليف الخاصة بنا.
stdstringwrapper فئة
الخطوة الأولى هي تحديد فصلنا الخاص في ملف stdstring.h. إذا كنت على دراية ببرمجة C ++ ، فلن تكون بالتأكيد غير معتاد على الخطين التاليين من التعليمات البرمجية.
#ifndef stdstring_h #define stdstring_h
هذا ينتمي إلى المعيار يشمل الحارس. بعد ذلك ، نحتاج إلى تضمين الرؤساء التاليين في فئة تضمين:
#يشمل
#يشمل
يستهدف أول واحد فئة STD :: String ، في حين أن الثانية تتضمن أعمالًا على جميع العقدة والمحتوى المرتبط بـ V8.
بعد اكتمال هذه الخطوة ، يمكننا إعلان فصلنا:
فئة stdstringwrapper: العقدة العامة :: ObjectWrap {
بالنسبة لجميع الفئات التي نعتزم تضمينها في البرنامج المساعد ، يجب علينا تمديد فئة العقدة :: ObjectWrap.
الآن يمكننا البدء في تحديد الممتلكات الخاصة لهذه الفئة:
Private: std :: string* s_ ؛ صريح stdstringWrapper (std :: string s = "") ؛ ~ stdstringWrapper () ؛
بالإضافة إلى المُنشئين والوظائف التحليلية ، نحتاج أيضًا إلى تحديد مؤشر لـ Std :: String. هذا هو جوهر هذه التقنية ويمكن استخدامه لتوصيل قاعدة رمز C/C ++ للعقدة - نحدد مؤشرًا خاصًا لفئة C/C ++ وسنستخدم هذا المؤشر لتنفيذ العمليات في جميع الطرق اللاحقة.
الآن نعلن أن الخاصية الثابتة المنشأة ، والتي ستوفر وظائف للفصل الذي أنشأناه في V8:
ثابت V8 :: مُنشئ ثابت ؛
يمكن للأصدقاء المهتمين النقر هنا للإشارة إلى خطة وصف القالب لمزيد من التفاصيل.
الآن نحتاج أيضًا إلى طريقة جديدة ، سيتم تعيينها إلى المُنشئ المذكور أعلاه ، وسيقوم V8 بتهيئة صفنا:
static v8 :: التعامل مع جديد (const v8 :: الحجج & args) ؛
يجب أن تتبع كل وظيفة تعمل على V8 المتطلبات التالية: ستقبل الإشارات إلى كائنات V8 :: الوسائط وإرجاع V8 :: Handle> v8 :: value>-وهذا هو بالضبط كيف يتعامل V8 مع JavaScript الضعيف عند استخدام الترميز القوي من النوع C ++.
بعد ذلك ، نحتاج إلى إدراج طريقتين أخريين في النموذج الأولي للكائن:
static v8 :: handle add (const v8 :: arguments & args) ؛ static v8 :: التعامل مع tostring (const v8 :: الحجج & args) ؛
حيث تتيح لنا طريقة ToString () الحصول على قيمة S_ بدلاً من قيمة [Object Object] عند استخدامها مع سلسلة JavaScript العادية.
أخيرًا ، سنقدم طريقة التهيئة (سيتم استدعاء هذه الطريقة بواسطة V8 وتعيينها لوظيفة المنشئ) وتشمل حارس الإغلاق:
العام: it static void init (v8 :: handleds orports) ؛ } ؛ #endif
دور كائن التصدير في وحدة JavaScript يعادل الوحدة النمطية.
ملف stdstring.cc ، وظائف مُنشئ ووظيفة التحليل
الآن إنشاء ملف stdstring.cc. نحتاج أولاً إلى تضمين رأسنا:
#include "stdstring.h"
ما يلي يحدد خاصية المنشئ (لأنه ينتمي إلى وظيفة ثابتة):
v8 :: stdstringwrapper المستمر :: مُنشئ ؛
سيقوم هذا المُنشئ الذي يخدم الفصل بتعيين سمة S_:
stdstringWrapper :: stdstringWrapper (std :: string s) {s_ = new std :: string (s) ؛ }وستقوم وظيفة التحليل بحذفها لتجنب فائض الذاكرة:
stdstringWrapper :: ~ stdstringWrapper () {delete s_ ؛ }بالإضافة إلى ذلك ، يجب عليك حذف جميع المحتوى المخصص لـ New ، لأنه في كل مرة قد تتسبب فيها هذا الموقف في استثناء ، يرجى تذكر العمليات المذكورة أعلاه أو استخدام مؤشر مشترك.
طريقة init
سيتم استدعاء هذه الطريقة بواسطة V8 ويهدف إلى تهيئة فئتنا (تعيين المُنشئ ، ووضع جميع المحتوى الذي نعتزم استخدامه في JavaScript في كائن الصادرات):
void stdstringWrapper :: init (v8 :: handleds) {
أولاً ، نحتاج إلى إنشاء قالب وظيفة لطريقتنا الجديدة:
v8 :: tpl local = v8 :: functionTemplate :: new (new) ؛
هذا يشبه إلى حد ما الوظيفة الجديدة في JavaScript - يسمح لنا بإعداد فصول JavaScript الخاصة بنا.
الآن يمكننا تعيين اسم للوظيفة وفقًا للاحتياجات الفعلية (إذا فاتتك هذه الخطوة ، فسيكون المنشئ في حالة مجهولة ، أي أن الاسم هو Somename () {} أو الدالة () {}):
tpl-> setClassName (v8 :: string :: newsymbol ("stdstring")) ؛
نستخدم V8 :: String :: NewsyMbol () لإنشاء سلسلة نوع خاصة لأسماء الخصائص - والتي توفر بعض الوقت لتشغيل المحرك.
بعد ذلك ، نحتاج إلى تعيين عدد الحقول التي تحتوي عليها مثيل الفصل لدينا:
TPL-> instancetemplate ()-> setinternalfieldCount (2) ؛
لدينا طريقتان - إضافة () و ToString () ، لذلك قمنا بتعيين الرقم على 2. يمكننا الآن إضافة أساليبنا الخاصة إلى النموذج الأولي للوظيفة:
tpl-> prototypeTemplate ()-> set (v8 :: string :: newsymbol ("add") ، v8 :: functionTemplate :: new (add)-> getFunction ()) ؛
tpl-> prototypeTemplate ()-> set (v8 :: string :: newsymbol ("toString") ، v8 :: functionTemplate :: new (toString)-> getFunction ()) ؛
يبدو هذا الجزء من الكود كبيرًا جدًا ، ولكن طالما لاحظت بعناية ، ستجد القواعد: نستخدم TPL-> النموذج الأولي ()-> set () لإضافة كل طريقة. نستخدم أيضًا v8 :: string :: newsymbol () لتزويدهم بالأسماء والوظيفة.
أخيرًا ، يمكننا وضع المُنشئ في كائن الصادرات داخل خصائص فئة المنشئ لدينا:
Constructor = v8 :: perperation :: new (tpl-> getFunction ()) ؛ orports-> set (v8 :: string :: newsymbol ("stdstring") ، مُنشئ) ؛ }طريقة جديدة
الآن ما يتعين علينا القيام به هو تحديد طريقة تعمل على شكل كائن javaScript.prototype.constructor:
v8 :: التعامل مع stdstringwrapper :: new (const v8 :: الحجج & args) {نحتاج أولاً إلى إنشاء نطاق لذلك:
V8 :: HandlesCope Scope ؛
بعد ذلك ، يمكننا استخدام طريقة .isconstructcall () لكائن args للتحقق مما إذا كان يمكن استدعاء المُنشئ باستخدام الكلمة الرئيسية الجديدة:
if (args.isconstructCall ()) {إذا استطعت ، نمرر أولاً المعلمة إلى Std :: String على النحو التالي:
v8 :: string :: utf8value str (args [0]-> toString ()) ؛ std :: string s (*str) ؛
... حتى نتمكن من نقله إلى مُنشئ فئتنا المغطاة:
stdstringWrapper* obj = جديد stdstringWrapper (s) ؛
بعد ذلك ، يمكننا استخدام طريقة .wrap () للكائن الذي أنشأناه مسبقًا (موروثة من Node :: ObjectWrap) لتعيينه لهذا المتغير:
obj-> wrap (args.this ()) ؛
أخيرًا ، يمكننا إعادة هذا الكائن الذي تم إنشاؤه حديثًا:
إرجاع args.This () ؛
إذا كان لا يمكن استدعاء الوظيفة باستخدام جديد ، فيمكننا أيضًا استدعاء المُنشئ مباشرة. بعد ذلك ، ما نريد القيام به هو تعيين ثابت لعدد المعلمات:
} آخر {const int argc = 1 ؛الآن نحتاج إلى إنشاء صفيف باستخدام المعلمات الخاصة بنا:
v8 :: argv local [argc] = {args [0]} ؛ثم قم بتمرير نتيجة طريقة المنشئ -> newinstance إلى SCOPE.CLOSE بحيث يمكن للكائن أن يلعب دورًا لاحقًا (Scope.Close يتيح للجميع بشكل أساسي الحفاظ عليها عن طريق تحريك مقبض معالجة الكائنات إلى نطاق أعلى - وهذا هو أيضًا الطريقة التي تسري بها الوظيفة):
Return Scope.Close (Constructor-> newInstance (Argc ، Argv)) ؛ }}
أضف الطريقة
الآن دعنا ننشئ طريقة إضافة ، والتي تهدف إلى السماح للجميع بإضافة محتوى إلى std :: string الداخلية للكائن:
v8 :: التعامل مع stdstringwrapper :: add (const v8 :: enduments & args) {أولاً ، نحتاج إلى إنشاء نطاق لوظائفنا وتحويل المعلمة إلى Std :: String كما كان من قبل:
V8 :: HandlesCope Scope ؛ v8 :: string :: utf8value str (args [0]-> toString ()) ؛ std :: string s (*str) ؛
الآن نحن بحاجة إلى فك الكائن. لقد أجرينا أيضًا عملية التغليف العكسي هذه من قبل - هذه المرة سنحصل على مؤشر إلى الكائن من هذا المتغير.
stdstringWrapper* obj = objectWrap :: unf (args.this ()) ؛
ثم يمكننا الوصول إلى سمة S_ واستخدام طريقة .append ():
obj-> s _-> إلحاق (s) ؛
أخيرًا ، نعيد القيمة الحالية لسمة S_ (تحتاج إلى استخدام Scope.Close مرة أخرى):
Return Scope.Close (v8 :: string :: new (obj-> s _-> c_str ())) ؛
نظرًا لأن طريقة V8 :: String :: New () يمكنها قبول مؤشر Char فقط كقيمة ، نحتاج إلى استخدام OBJ-> S _-> C_STR () للحصول عليها.
في هذا الوقت ، يجب إنشاء دليل بناء في مجلد المكونات الإضافية.
امتحان
الآن يمكننا اختبار المكونات الإضافية لدينا. قم بإنشاء ملف test.js ومكتبات التجميع اللازمة في دليل الإضافات لدينا (يمكنك تخطي امتداد .Node مباشرة):
var addon = require ('./ build/release/addon') ؛بعد ذلك ، قم بإنشاء مثيل جديد لكائننا:
var test = new addon.stdstring ('test') ؛بعد ذلك ، افعل ذلك ، مثل إضافة أو تحويله إلى سلسلة:
test.add ('!') ؛ console.log ('test/' s contents: ٪ s '، test) ؛بعد الجري ، يجب أن ترى نتائج التنفيذ التالية في وحدة التحكم:
ختاماً
آمل أنه بعد قراءة هذا البرنامج التعليمي ، يمكنك تبديد مخاوفك واعتبر إنشاء واختبار المكونات الإضافية Node.js المخصصة استنادًا إلى مكتبات C/C ++ كمهمة صعبة للغاية. يمكنك استخدام هذه التقنية لإدخال أي مكتبة C/C ++ بسهولة تقريبًا في Node.js. إذا كنت تريد ، يمكنك أيضًا إضافة المزيد من الوظائف إلى المكون الإضافي وفقًا للاحتياجات الفعلية. يوفر Std :: String الكثير من الطرق ، ويمكننا استخدامها كمواد تمرين.
روابط عملية
يمكن للأصدقاء المهتمين التحقق من الروابط التالية لمزيد من الموارد والتفاصيل المتعلقة بتطوير المكونات الإضافية Node.js ، ومكتبات حلقة الأحداث V8 و C.
• وثائق Node.js البرنامج المساعد
• توثيق V8
• Libuv (مكتبة حلقة الأحداث) ، من Github
اللغة الإنجليزية: http://code.tutsplus.com/tutorials/writing-nodejs-addons-cms-21771