Programmēšana

Java 17. padoms: Java integrēšana ar C ++

Šajā rakstā es apspriedīšu dažus jautājumus, kas saistīti ar C ++ koda integrēšanu ar Java lietojumprogrammu. Pēc vārda par to, kāpēc kāds to vēlas darīt un kādi ir daži šķēršļi, es izveidošu darbojošos Java programmu, kas izmanto objektus, kas rakstīti C ++. Pa ceļam es apspriedīšu dažas no sekām, kādas varētu būt šādai rīcībai (piemēram, mijiedarbība ar atkritumu savākšanu), un iepazīstināšu ar ieskatu, ko mēs šajā jomā varam sagaidīt nākotnē.

Kāpēc integrēt C ++ un Java?

Kāpēc jūs vispār vēlaties integrēt C ++ kodu Java programmā? Galu galā Java valoda tika izveidota daļēji, lai novērstu dažus C ++ trūkumus. Patiesībā ir vairāki iemesli, kāpēc jūs vēlaties integrēt C ++ ar Java:

  • Izrāde. Pat ja jūs veidojat platformu ar tieši laikā sagatavotu (JIT) kompilatoru, izredzes ir tādas, ka JIT izpildlaika ģenerētais kods ir ievērojami lēnāks nekā ekvivalents C ++ kods. Uzlabojoties JIT tehnoloģijai, tam vajadzētu kļūt mazāk faktoram. (Faktiski tuvākajā nākotnē laba JIT tehnoloģija var nozīmēt, ka Java darbojas ātrāk nekā ekvivalents C ++ kods.)
  • Mantotā koda atkārtotai izmantošanai un integrēšanai mantotajās sistēmās.
  • Lai tieši piekļūtu aparatūrai vai veiktu citas zema līmeņa darbības.
  • Izmantot rīkus, kas vēl nav pieejami Java (pieaugušie OODBMSes, ANTLR un tā tālāk).

Ja jūs nolemjat kritienu un nolemjat integrēt Java un C ++, jūs atsakāties no dažām svarīgām tikai Java lietojumprogrammas priekšrocībām. Šeit ir negatīvās puses:

  • Jauktu C ++ / Java lietojumprogrammu nevar palaist kā sīklietotni.
  • Jūs atsakāties no rādītāju drošības. Jūsu C ++ kods var brīvi maldināt objektus, piekļūt izdzēstam objektam vai sabojāt atmiņu citos citos veidos, kas C ++ ir tik vienkārši.
  • Jūsu kods, iespējams, nav pārnēsājams.
  • Jūsu izveidotā vide noteikti nebūs pārnēsājama - jums būs jāizdomā, kā ievietot C ++ kodu koplietojamā bibliotēkā visās interesējošajās platformās.
  • C un Java integrēšanas API ir izstrādes stadijā un, visticamāk, mainīsies, pārejot no JDK 1.0.2 uz JDK 1.1.

Kā redzat, Java un C ++ integrēšana nav domāta vājprātam! Tomēr, ja vēlaties turpināt, lasiet tālāk.

Mēs sāksim ar vienkāršu piemēru, parādot, kā izsaukt C ++ metodes no Java. Pēc tam mēs paplašināsim šo piemēru, lai parādītu, kā atbalstīt novērotāju modeli. Novērotāja modelis papildus tam, ka tas ir viens no objektorientētās programmēšanas stūrakmeņiem, kalpo kā jauks piemērs vairāk iesaistītajiem C ++ un Java koda integrēšanas aspektiem. Pēc tam mēs izveidosim nelielu programmu, lai pārbaudītu mūsu Java iesaiņoto C ++ objektu, un mēs beigsim ar turpmāko Java virzienu apspriešanu.

Zvanīšana uz C ++ no Java

Kas ir tik grūti integrējot Java un C ++, jūs jautājat? Galu galā, SunSoft's Java apmācība ir sadaļa "Vietējo metožu integrēšana Java programmās" (skatiet resursus). Kā redzēsim, tas ir piemērots, lai izsauktu C ++ metodes no Java, taču tas nedod mums pietiekami daudz, lai izsauktu Java metodes no C ++. Lai to izdarītu, mums būs jāpaveic nedaudz vairāk darba.

Kā piemēru mēs ņemsim vienkāršu C ++ klasi, kuru mēs vēlētos izmantot no Java. Mēs pieņemsim, ka šī klase jau pastāv un ka mums nav atļauts to mainīt. Šo klasi sauc par "C ++ :: NumberList" (skaidrības labad es visus C ++ klases nosaukumus pievienošu prefiksiem "C ++ ::"). Šī klase ievieš vienkāršu numuru sarakstu ar metodēm, kā pievienot numuru sarakstam, vaicāt saraksta lielumam un iegūt elementu no saraksta. Mēs izveidosim Java klasi, kuras uzdevums ir pārstāvēt C ++ klasi. Šai Java klasei, kuru mēs sauksim par NumberListProxy, būs tās pašas trīs metodes, taču šo metožu ieviešana būs C ++ ekvivalentu izsaukšana. Tas ir attēlots šajā objektu modelēšanas tehnikas (OMT) diagrammā:

Java eksemplāram NumberListProxy ir jāsaglabā atsauce uz atbilstošo NumberList C ++ gadījumu. Tas ir pietiekami viegli, ja nedaudz nepārnēsājams: ja atrodamies platformā ar 32 bitu rādītājiem, mēs varam vienkārši saglabāt šo rādītāju int; ja atrodamies platformā, kas izmanto 64 bitu rādītājus (vai domājam, ka varētu būt tuvākajā nākotnē), mēs tos varam glabāt ilgi. Faktiskais NumberListProxy kods ir vienkāršs, kaut arī nedaudz netīrs. Tas izmanto SunSoft Java apmācības sadaļas "Vietējo metožu integrēšana Java programmās" mehānismus.

Pirmais griezums Java klasē izskatās šādi:

 publiskā klase NumberListProxy {static {System.loadLibrary ("NumberList"); } NumberListProxy () {initCppSide (); } public native void addNumber (int n); valsts dzimtā int lielums (); valsts dzimtā int getNumber (int i); privāts dzimtene void initCppSide (); private int numberListPtr_; // Numuru saraksts *} 

Statiskā sadaļa tiek palaista, kad klase ir ielādēta. System.loadLibrary () ielādē nosaukto koplietojamo bibliotēku, kurā mūsu gadījumā ir apkopota C ++ :: NumberList versija. Sadaļā Solaris tā cer atrast koplietojamo bibliotēku "libNumberList.so" kaut kur $ LD_LIBRARY_PATH. Koplietojamo bibliotēku nosaukšanas kārtība citās operētājsistēmās var atšķirties.

Lielākā daļa šīs klases metožu tiek pasludinātas par "vietējām". Tas nozīmē, ka mēs to nodrošināsim ar C funkciju. Lai rakstītu C funkcijas, mēs divreiz palaidām javah, vispirms kā "javah NumberListProxy", pēc tam kā "javah -stubs NumberListProxy". Tas automātiski ģenerē Java izpildlaikam nepieciešamo "līme" kodu (ko tas ievieto NumberListProxy.c) un ģenerē deklarācijas C funkcijām, kuras mums jāievieš (in NumberListProxy.h).

Es izvēlējos šīs funkcijas ieviest failā ar nosaukumu NumberListProxyImpl.cc. Tas sākas ar dažām tipiskām direktīvām #include:

 // // NumberListProxyImpl.cc // // // Šis fails satur C ++ kodu, kas ievieš cēloņus, kurus // rada "javah -stubs NumberListProxy". sal. NumberListProxy.c. #include #include "NumberListProxy.h" #include "NumberList.h" 

ir daļa no JDK un ietver vairākas svarīgas sistēmas deklarācijas. NumberListProxy.h mums izveidoja javah, un tajā iekļautas to C funkciju deklarācijas, kuras mēs gatavojamies rakstīt. NumberList.h satur C ++ klases NumberList deklarāciju.

Konstruktorā NumberListProxy mēs izsaucam vietējo metodi initCppSide (). Šai metodei jāatrod vai jāizveido C ++ objekts, kuru mēs vēlamies attēlot. Šī raksta vajadzībām es vienkārši uzkrāju jaunu C ++ objektu, lai gan parasti mēs varētu vēlēties saistīt mūsu starpniekserveri ar C ++ objektu, kas tika izveidots citur. Mūsu vietējās metodes ieviešana izskatās šādi:

 void NumberListProxy_initCppSide (struct HNumberListProxy * javaObj) {NumberList * list = new NumberList (); unand (javaObj) -> numberListPtr_ = (garš) saraksts; } 

Kā aprakstīts Java apmācība, mēs esam nodevuši "rokturi" objektam Java NumberListProxy. Mūsu metode izveido jaunu C ++ objektu, pēc tam to pievieno Java objekta numberListPtr_ datu dalībniekam.

Tagad par interesantajām metodēm. Šīs metodes atgūst rādītāju objektam C ++ (no datu dalībnieka numberListPtr_) un pēc tam izsauc vēlamo C ++ funkciju:

 void NumberListProxy_addNumber (struct HNumberListProxy * javaObj, long v) {NumberList * list = (NumberList *) unand (javaObj) -> numberListPtr_; saraksts-> addNumber (v); } garš NumberListProxy_size (struct HNumberListProxy * javaObj) {NumberList * list = (NumberList *) neatklāts (javaObj) -> numberListPtr_; atgriešanās saraksts-> izmērs (); } garš NumberListProxy_getNumber (struct HNumberListProxy * javaObj, garš i) {NumberList * list = (NumberList *) unand (javaObj) -> numberListPtr_; atgriešanās saraksts-> getNumber (i); } 

Funkciju nosaukumus (NumberListProxy_addNumber un pārējos) mums nosaka javah. Lai iegūtu papildinformāciju par to, funkcijai nosūtīto argumentu veidus, makro unand () un citu informāciju par Java atbalstu vietējām C funkcijām, lūdzu, skatiet Java apmācība.

Lai gan šī "līme" ir nedaudz garlaicīga rakstīšanai, tā ir diezgan vienkārša un darbojas labi. Bet kas notiek, ja mēs vēlamies izsaukt Java no C ++?

Zvanīšana uz Java no C ++

Pirms iedziļināties lai izsauktu Java metodes no C ++, ļaujiet man paskaidrot kāpēc tas var būt vajadzīgs. Diagrammā, kuru parādīju iepriekš, es neuzrādīju visu C ++ klases stāstu. Pilnīgāks C ++ klases attēls ir parādīts zemāk:

Kā redzat, mums ir darīšana ar novērojamu numuru sarakstu. Šis numuru saraksts var tikt modificēts daudzās vietās (no NumberListProxy vai no jebkura C ++ objekta, kam ir atsauce uz mūsu C ++ :: NumberList objektu). Paredzams, ka NumberListProxy patiesi pārstāv visi par C ++ uzvedību :: NumberList; tajā jāiekļauj Java novērotāju paziņošana, kad mainās numuru saraksts. Citiem vārdiem sakot, NumberListProxy jābūt java.util apakšklasei. Novērojams, kā parādīts šeit:

Tas ir pietiekami vienkārši, lai padarītu NumberListProxy par java.util apakšklasi. Novērojams, bet kā par to tiek paziņots? Kurš izsauks setChanged () un paziņosObservers (), kad mainīsies C ++ :: NumberList? Lai to izdarītu, mums būs nepieciešama palīgu klase C ++ pusē. Par laimi šī viena palīga klase strādās ar jebkuru novērojamu Java. Šai palīgu klasei jābūt C ++ :: Observer apakšklasei, lai tā varētu reģistrēties vietnē C ++ :: NumberList. Kad numuru saraksts mainās, tiks izsaukta mūsu palīgu klases atjaunināšanas () metode. Mūsu atjaunināšanas () metodes ieviešana būs Java starpniekservera objekta izsaukšana setChanged () un paziņošanaObservers (). Tas ir attēlots OMT:

Pirms ķerties pie C ++ :: JavaObservableProxy ieviešanas, ļaujiet man pieminēt dažas citas izmaiņas.

NumberListProxy ir jauns datu biedrs: javaProxyPtr_. Šis ir kursors uz C ++ JavaObservableProxy gadījumu. Tas mums būs vajadzīgs vēlāk, kad mēs apspriedīsim objektu iznīcināšanu. Vienīgās izmaiņas mūsu esošajā kodā ir izmaiņas mūsu C funkcijā NumberListProxy_initCppSide (). Tagad tas izskatās šādi:

 void NumberListProxy_initCppSide (struct HNumberListProxy * javaObj) {NumberList * list = new NumberList (); struct HObservable * novērojams = (struct HObservable *) javaObj; JavaObservableProxy * proxy = jauns JavaObservableProxy (novērojams, saraksts); unand (javaObj) -> numberListPtr_ = (garš) saraksts; unand (javaObj) -> javaProxyPtr_ = (garš) starpniekserveris; } 

Ņemiet vērā, ka mēs javaObj nododam rādītājam uz HObservable. Tas ir labi, jo mēs zinām, ka NumberListProxy ir Observable apakšklase. Vienīgās citas izmaiņas ir tādas, ka tagad mēs izveidojam C ++ :: JavaObservableProxy gadījumu un saglabājam atsauci uz to. C ++ :: JavaObservableProxy tiks rakstīts tā, lai tas paziņotu visiem Java Observable, kad tas atklāj atjauninājumu, tāpēc mums HNumberListProxy * bija jāraida uz HObservable *.

Ņemot vērā līdzšinējo fonu, var šķist, ka mums vienkārši jāievieš C ++ :: JavaObservableProxy: update () tā, lai tas paziņotu par novērojamo Java. Šis risinājums šķiet konceptuāli vienkāršs, taču ir aizķeršanās: Kā mēs turam atsauci uz Java objektu no C ++ objekta?

Java atsauces uzturēšana C ++ objektā

Var šķist, ka mēs varētu vienkārši saglabāt Java objekta rokturi C ++ objektā. Ja tas tā būtu, mēs varētu kodu C ++ :: JavaObservableProxy šādi:

 klases JavaObservableProxy publiskais novērotājs {public: JavaObservableProxy (struct HObservable * javaObj, Observable * obs) {javaObj_ = javaObj; novērotsViens_ = obs; observOne _-> addObserver (šis); } ~ JavaObservableProxy () {observOne _-> deleteObserver (šis); } void update () {execute_java_dynamic_method (0, javaObj_, "setChanged", "() V"); } privāts: struct HObservable * javaObj_; Novērojams * novērotsViens_; }; 

Diemžēl mūsu dilemmas risinājums nav tik vienkāršs. Kad Java jums nodos rokturi Java objektam, rokturis] paliks derīgs uz zvana laiku. Tas ne vienmēr būs derīgs, ja jūs to glabājat kaudzē un mēģināsit to izmantot vēlāk. Kāpēc tas tā ir? Java atkritumu savākšanas dēļ.

Pirmkārt, mēs cenšamies saglabāt atsauci uz Java objektu, bet kā Java izpildlaiks zina, ka mēs saglabājam šo atsauci? Tā nav. Ja nevienam Java objektam nav atsauces uz objektu, atkritumu savācējs to var iznīcināt. Šajā gadījumā mūsu C ++ objektam būtu piekārta atsauce uz atmiņas apgabalu, kurā agrāk bija derīgs Java objekts, bet tagad tas varētu saturēt kaut ko pavisam citu.

Pat ja mēs esam pārliecināti, ka mūsu Java objekts nesavāks atkritumus, pēc kāda laika mēs joprojām nevaram uzticēties Java objekta rokturim. Atkritumu savācējs, iespējams, nenoņem Java objektu, taču tas varētu ļoti labi pārvietot to uz citu atmiņas vietu! Java specifikācijā nav garantijas pret šo parādību. Sun JDK 1.0.2 (vismaz zem Solaris) šādā veidā nepārvietos Java objektus, taču citiem izpildlaikiem nav garantiju.

Mums patiešām ir nepieciešams veids, kā informēt atkritumu savācēju, ka mēs plānojam saglabāt atsauci uz Java objektu un lūgt kaut kādu "globālu atsauci" uz Java objektu, kas garantēti paliek derīgs. Diemžēl JDK 1.0.2 nav šāda mehānisma. (Viens, iespējams, būs pieejams JDK 1.1; plašāku informāciju par turpmākajiem virzieniem skatiet šī raksta beigās.) Kamēr mēs gaidām, mēs varam novērst šo problēmu.

$config[zx-auto] not found$config[zx-overlay] not found