Bagaimana cara merancang program C ++ untuk memungkinkan impor runtime fungsi?

10

hari ini, saya ingin mengajukan pertanyaan kepada Anda tentang kemampuan C ++ untuk mewujudkan arsitektur perangkat lunak tertentu.

Tentu saja, saya telah menggunakan pencarian tetapi belum menemukan jawaban yang terhubung langsung.

Pada dasarnya, tujuan saya adalah untuk membangun sebuah program yang memungkinkan pengguna untuk pemodelan dan simulasi sistem fisik yang disusun secara sewenang-wenang, misalnya mobil pengendara. Saya berasumsi memiliki perpustakaan model fisik (fungsi dalam kelas). Setiap fungsi dapat memiliki beberapa input dan mengembalikan beberapa output tergantung pada deskripsi fisik yang mendasarinya, misalnya model mesin pembakaran, model drag aerodinamis, model roda, dll.

Sekarang, idenya adalah untuk memberikan pengguna kerangka kerja yang memungkinkan dia untuk menyusun fungsi sesuai dengan kebutuhannya, yaitu untuk memetakan setiap perilaku fisik. Kerangka kerja harus menyediakan fungsionalitas untuk menghubungkan output dan input dari berbagai fungsi. Oleh karena itu, kerangka kerja menyediakan kelas wadah. Saya menyebutnya KOMPONEN, yang dapat menampung satu atau banyak objek model (FUNGSI). Wadah ini juga dapat menampung komponen lainnya (lih. Pola komposit) serta koneksi (KONEKTOR) antara parameter fungsi. Selain itu, kelas komponen menyediakan beberapa fungsi numerik umum seperti pemecah matematika dan sebagainya.

Komposisi fungsi harus dilakukan selama runtime. Pada contoh pertama, pengguna harus dapat mengatur komposisi dengan mengimpor XML yang mendefinisikan struktur komposisi. Kemudian, orang bisa berpikir untuk menambahkan GUI.

Untuk memberi Anda pemahaman yang lebih baik di sini adalah contoh yang sangat sederhana:

<COMPONENT name="Main">
  <COMPONENT name="A">
    <FUNCTION name="A1" path="lib/functionA1" />
  </COMPONENT>
  <COMPONENT name="B">
    <FUNCTION name="B1" path="lib/functionB1" />
    <FUNCTION name="B2" path="lib/functionB2" />
  </COMPONENT>
  <CONNECTIONS>
    <CONNECTOR source="A1" target="B1" />
    <CONNECTOR source="B1" target="B2" />
  </CONNECTIONS>        
</COMPONENT>

Tidak perlu menyelami lebih dalam kemampuan kerangka kerja karena masalah saya jauh lebih umum. Ketika kode / program kerangka dikompilasi, deskripsi masalah fisik, serta fungsi yang ditentukan pengguna, tidak diketahui. Ketika pengguna memilih (melalui XML atau lebih baru melalui GUI) suatu fungsi, kerangka kerja harus membaca informasi fungsi, yaitu harus mendapatkan informasi parameter input dan output, untuk menawarkan kepada pengguna opsi untuk menghubungkan fungsi-fungsi tersebut.

Saya tahu prinsip-prinsip refleksi dan saya sadar bahwa C ++ tidak menyediakan fitur ini. Namun, saya yakin bahwa konsep "membangun objek selama runtime" sangat sering diperlukan. Bagaimana saya harus mengatur arsitektur perangkat lunak saya di C ++ untuk mencapai tujuan saya? Apakah C ++ bahasa yang tepat? Apa yang saya abaikan?

Terima kasih sebelumnya!

Cheers, Oliver

Oliver
sumber
C ++ memang memiliki fungsi pointer dan objek fungsi. Apakah semua fungsi dikompilasi ke dalam executable, atau apakah itu di perpustakaan dinamis (pada platform apa)?
Caleth
1
Pertanyaannya terlalu luas dalam arti bahwa biasanya memerlukan gelar sarjana di bidang teknik listrik / [otomatisasi desain elektronik (EDA)] ( en.wikipedia.org/wiki/Electronic_design_automation ) atau teknik mesin / desain bantuan komputer (CAD) . Secara komparatif, memanggil C / C ++ dynamic library sangat mudah, lihat konvensi pemanggilan C untuk x86 . Mungkin perlu memanipulasi tumpukan (melalui penunjuk tumpukan CPU) dan nilai register CPU.
rwong
1
Fungsi memuat dinamis tidak didukung oleh bahasa C ++. Anda harus melihat sesuatu yang spesifik platform. Sebagai contoh kompiler C ++ pada Windows harus mendukung Windows DLL, yang mendukung bentuk refleksi.
Simon B
Dalam C ++ sangat sulit untuk memanggil fungsi yang tanda tangannya (argumen dan tipe pengembalian) tidak diketahui pada waktu kompilasi. Untuk melakukannya, Anda perlu tahu cara kerja pemanggilan fungsi di tingkat perakitan platform yang Anda pilih.
Bart van Ingen Schenau
2
Cara saya mengatasi ini adalah dengan mengkompilasi kode c ++ yang menciptakan juru bahasa untuk setiap bahasa yang mendukung perintah eval. Masalah Bang diselesaikan menggunakan c ++. : P Tolong pikirkan mengapa itu tidak cukup baik dan perbarui pertanyaan. Ini membantu ketika persyaratan sebenarnya jelas.
candied_orange

Jawaban:

13

Dalam standar C + + murni, Anda tidak dapat "mengizinkan impor runtime fungsi"; sesuai dengan standar, himpunan fungsi C ++ dikenal secara statis pada waktu-bangun (dalam praktiknya, waktu-tautan) karena diperbaiki dari gabungan semua unit terjemahan yang menyusun program Anda.

Dalam praktiknya, sebagian besar waktu (tidak termasuk sistem tertanam) program C ++ Anda berjalan di atas beberapa sistem operasi . Baca Sistem Operasi: Tiga Potong Mudah untuk ikhtisar yang baik.

Beberapa sistem operasi modern memungkinkan pembebanan dinamis dari plugin . POSIX secara khusus menentukan dlopen& dlsym. Windows memiliki sesuatu yang berbeda LoadLibrary(dan model penghubung yang lebih rendah; Anda perlu secara eksplisit menjelaskan fungsi yang terkait, disediakan, atau digunakan oleh plugin). BTW di Linux, Anda bisa dlopenmemiliki banyak plugin (lihat manydl.cprogram saya , dengan kesabaran yang cukup, bisa menghasilkan kemudian memuat hampir satu juta plugin). Jadi, XML Anda bisa mendorong pemuatan plugin. Deskripsi multi-komponen / multi-konektor Anda mengingatkan saya pada sinyal dan slot Qt (yang memerlukan mocpreprosesor ; Anda mungkin perlu sesuatu seperti itu juga).

Sebagian besar implementasi C ++ menggunakan nama mangling . Karena itu, Anda akan lebih baik mendeklarasikan sebagai extern "C"fungsi yang terkait dengan plugin (dan didefinisikan di dalamnya, dan diakses oleh dlsymdari program utama). Baca C ++ dlopen mini HowTo (setidaknya untuk Linux).

BTW, Qt dan POCO adalah kerangka kerja C ++ yang menyediakan beberapa pendekatan portabel dan tingkat tinggi untuk plugin. Dan libffi memungkinkan Anda untuk memanggil fungsi yang tanda tangannya hanya diketahui saat runtime.

Kemungkinan lain adalah menanamkan beberapa penerjemah, seperti Lua atau Guile , dalam program Anda (atau menulis sendiri, seperti yang dilakukan Emacs). Ini adalah keputusan desain arsitektur yang kuat. Anda mungkin ingin membaca Lisp Dalam Potongan Kecil dan Memprogram Pragmatik Bahasa untuk lebih lanjut.

Ada varian atau campuran dari pendekatan-pendekatan itu. Anda dapat menggunakan beberapa pustaka kompilasi JIT (seperti libgccjit atau asmjit). Anda dapat menghasilkan pada saat runtime beberapa kode C dan C ++ dalam file sementara, mengkompilasinya sebagai plugin sementara, dan secara dinamis memuat plugin itu (saya menggunakan pendekatan seperti itu di GCC MELT ).

Dalam semua pendekatan ini, manajemen memori adalah masalah penting (ini adalah properti "seluruh program", dan apa yang sebenarnya merupakan "amplop" dari program Anda adalah "berubah"). Setidaknya Anda membutuhkan budaya tentang pengumpulan sampah . Baca buku pegangan GC untuk terminologinya. Dalam banyak kasus ( referensi sirkuler arbitrer di mana pointer lemah tidak dapat diprediksi), skema penghitungan referensi yang disukai oleh C ++ smart pointer mungkin tidak cukup. Lihat juga ini .

Baca juga tentang pembaruan perangkat lunak dinamis .

Perhatikan bahwa beberapa bahasa pemrograman, terutama Common Lisp (dan Smalltalk ), lebih bersahabat dengan gagasan fungsi impor runtime. SBCL adalah implementasi perangkat lunak bebas Lisp gratis, dan mengkompilasi ke kode mesin di setiap interaksi REPL (dan bahkan dapat mengumpulkan sampah kode mesin, dan dapat menyimpan seluruh file gambar inti yang nantinya dapat dengan mudah dimulai kembali).

Basile Starynkevitch
sumber
3

Jelas Anda mencoba untuk menggulung gaya Anda sendiri dari perangkat lunak jenis Simulink atau LabVIEW, tetapi dengan komponen XML yang tidak suci.

Pada dasarnya, Anda mencari struktur data yang berorientasi grafik. Model fisik Anda terdiri dari node (Anda menyebutnya komponen), dan edge (konektor dalam penamaan Anda).

Tidak ada mekanisme yang dipaksakan untuk melakukan ini, bahkan dengan refleksi, jadi Anda harus membuat API dan komponen apa pun yang ingin dimainkan harus menerapkan beberapa fungsi dan mematuhi aturan yang ditetapkan oleh API Anda.

Setiap komponen perlu mengimplementasikan serangkaian fungsi untuk melakukan hal-hal seperti:

  • Dapatkan nama komponen atau detail lainnya tentangnya
  • Dapatkan jumlah berapa banyak input atau output yang diekspos komponen
  • Menginterogasi komponen tentang input tertentu output kami
  • Hubungkan input dan output bersama
  • dan lain-lain

Dan itu hanya untuk mengatur grafik Anda. Anda akan memerlukan fungsi tambahan yang ditetapkan untuk mengatur bagaimana model Anda sebenarnya dieksekusi. Setiap fungsi akan memiliki nama tertentu dan semua komponen harus memiliki fungsi tersebut. Apa pun yang spesifik untuk suatu komponen harus dapat dijangkau melalui API itu, dengan cara yang identik dari komponen ke komponen.

Program Anda seharusnya tidak mencoba memanggil 'fungsi yang ditentukan pengguna' ini. Alih-alih, ia harus memanggil fungsi 'komputasi' tujuan umum atau semacamnya pada setiap komponen, dan komponen itu sendiri yang memanggil fungsi tersebut, dan mentransformasikan inputnya ke outputnya. Koneksi input dan output adalah abstraksi untuk fungsi itu, itulah satu-satunya hal yang harus dilihat oleh program.

Singkatnya, sedikit dari ini sebenarnya khusus untuk C ++, tetapi Anda harus menerapkan semacam jenis informasi run-time, disesuaikan dengan domain masalah khusus Anda. Dengan setiap fungsi yang didefinisikan oleh API, Anda akan tahu nama fungsi apa yang harus dipanggil saat runtime, dan Anda akan tahu tipe data dari masing-masing panggilan, dan Anda hanya menggunakan pemuatan pustaka dinamis lama yang biasa untuk menyelesaikannya. Ini akan datang dengan jumlah boiler yang cukup, tapi itu hanya bagian dari kehidupan.

Satu aspek khusus C ++ yang ingin Anda ingat adalah yang terbaik untuk menjadikan API Anda sebagai API C sehingga Anda dapat menggunakan kompiler yang berbeda untuk modul yang berbeda, jika pengguna menyediakan modul mereka sendiri.

DirectShow adalah API yang melakukan semua yang saya jelaskan dan bisa menjadi contoh yang baik untuk dilihat.

Apa namanya
sumber