Ці бяспечна выкарыстоўваць радкі ў якасці прыватных элементаў дадзеных у класе, выкарыстоўваным праз DLL boundry?

Я разумею, што выкрыццё функцыі, якія прымаюць або вяртаюць СТЛИ кантэйнеры (напрыклад, станд :: радок ) праз мяжу DLL можа выклікаць праблемы з-за адрозненні ў STL рэалізацыях гэтых кантэйнераў у 2-х двайковых файлах. Але гэта бяспечна экспартаваць клас, як:

class Customer
{
public:
  wchar_t * getName() const;

private:
  wstring mName;
};

Без якога-то хаку, mName не збіраецца быць карыснымі ў выкананым файле, так што ён не будзе ў стане выканаць метады на mName, ні будаваць/разбураць гэты аб'ект.

Мая кішка пачуццё «не робіць гэтага, гэта небяспечна», але я не магу зразумець, добрая нагода.

1
@JAB некаторыя рэчы з'яўляюцца стандартнымі, іншыя няма. Калі STL вызначае клас адрас , які мае сябры IP і порт , ён не можа паказаць, што ф папярэднічае порт у памяці, і ня necescarily кажуць, што не могуць быць іншыя ня спекуляцыя пэўныя элементы (напрыклад, ip_version ). Такім чынам, пры пераходзе цалкам сканструяваны аб'ект паміж двайковымі файламі, адзін можа выклалі аб'ект як [ф] [порт] у памяці, у той час як іншыя могуць чакаць, што гэта будзе [порт] [ф] у памяці - яны не маюць ніякага спосабу сказаць адзін аднаго аб адрозненні. @riv, добры пункт аб новы (ці Таноса ) трэба ведаць памер аб'екта!
дададзена аўтар Rollie, крыніца
Як стварыць асобнікі класа? Калі вы выкарыстоўваеце завод (рэалізаваны ў DLL), вы павінны быць добра. У адваротным выпадку, калі вы ствараеце іх новы або ў стэку, і wstring здараецца быць рознага памеру, вы сутыкнецеся з праблемамі.
дададзена аўтар riv, крыніца
Разумею. Падобна на тое, абгортка, якая выстаўляе C-сумяшчальны інтэрфейс для патрэбных метадаў будзе лепшым выбарам, то (што я на самой справе ў канчатковым выніку робіць у мінулым годзе пры выкарыстанні llvmpy і выявіў, што некаторыя вельмі карысныя рэчы ў базавай LLVM API не былі рэалізаваныя, у гэты момант я ў канчатковым выніку напісанне некаторых абгортак для доступу з ctypes як я не меў ніякага вопыту работы з SWIG [да гэтага часу не маюць які-небудзь, на самай справе., верагодна, варта зафіксаваць, што ў нейкі момант].) ... на самай справе, робяць падтрымку SWIG C ++/C ++ прывязак для падобных выпадкаў?
дададзена аўтар JAB, крыніца
Я думаў, што кропка стандартных кампанентаў бібліятэкі павінны была быць стандартнымі. Ці ёсць няяўнай «для канкрэтнага кампілятара» у там?
дададзена аўтар JAB, крыніца
@JAB Карацей кажучы, не існуе стандартны C ++ ABI. См stackoverflow.com/q/2083060/1639256 і stackoverflow.com/a/7492291/1639256 .
дададзена аўтар Oktalist, крыніца

6 адказы

Гэта не праблема. Таму што яна перайграла па </вялікіх STRONG> праблем, вы не можаце стварыць аб'ект гэтага класа ў кодзе, які жыве ў іншым модулі, чым той, які змяшчае код для класа. Код у іншым модулі не можа дакладна ведаць патрабаваны памер аб'екта, іх рэалізацыя класа станд :: радкі цалкам можа быць розным. Які, як было заяўлена, а таксама ўплывае на памер аб'екта Customer. Нават той жа кампілятар не можа гарантаваць гэта, змешваючыся аптымізаваны і адладка зборкі гэтых модуляў, напрыклад. Albeit, што гэта, як правіла, даволі лёгка пазбегнуць.

Такім чынам, вы <�моцны> павінен стварыць фабрыку класаў для аб'ектаў кліентаў, завод, які жыве ў тым жа модулі. Што аўтаматычна азначае, што <�ет> любы код, які тычыцца члена «mName» таксама жыве ў тым жа модулі. І таму ў бясьпецы.

Наступны крок, то гэта не выставіць кліентаў на ўсіх, але выкрыць чысты абстрактны базавы клас (так званы інтэрфейс). Цяпер вы можаце прадухіліць код кліента ад стварэння асобніка Кліента і здымаць іх нагу. І вы будзеце трывіяльным схаваць зЬй :: радок, а таксама. Інтэрфейс на аснове метадаў праграмавання з'яўляюцца агульнымі для сцэнарыяў модуль для ўзаемадзеяння з іншымі. Акрамя таго, падыход, COM.

5
дададзена

Нават калі ваш клас не маюць элементаў дадзеных, вы не можаце чакаць, што яна будзе выкарыстоўвацца з кода, скампіляванага з іншага кампілятара. Там няма адзінага ABI для класаў C ++. Вы можаце разлічваць на адрозненні ў назве перакручваючы толькі для пачатку.

Калі вы гатовыя абмежаваць кліентаў выкарыстоўваць той жа кампілятар, як вы, ці даць крыніца, каб дазволіць кліентам кампіляцыі кода з іх кампілятарам, то вы можаце зрабіць амаль усё, што праз ваш інтэрфейс. У адваротным выпадку вы павінны прытрымлівацца інтэрфейсаў C стылю.

1
дададзена

As long as the allocator of instances of the class and deallocator are of the same settings, you should be ok, but you are right to avoid this.
Differences between the .exe and .dll as far as debug/release, code generation (Multi-threaded DLL vs. Single threaded) could cause problems in some scenarios.
I would recommend using abstract classes in the DLL interface with creation and deletion done solely inside the DLL.
Interfaces like:

class A {
protected:
  virtual ~A() {}
public:
  virtual void func() = 0;
};

//exported create/delete functions
A* create_A();
void destroy_A(A*);

DLL рэалізацыі, як:

class A_Impl : public A{
public:
  ~A_Impl() {}
  void func() { do_something(); }
}

A* create_A() { return new A_Impl; }
void destroy_A(A* a) { 
  A_Impl* ai=static_cast(a);
  delete ai;
}

Павінна быць нармальна.

1
дададзена
@Photon гэта толькі калі вы відавочнай выявай вызначыць канкрэтную функцыю ў якасці ўбудаванай ў загалоўку, правільна?
дададзена аўтар Rollie, крыніца
Вы ўпэўненыя, што Шматструменнасць/DLL/і г.д. пытанні праблема? Я не бачу, як, так як увесь код, звязаны з аб'ектам кантэйнера гарантавана будзе выконвацца з выкарыстаннем падчас выканання версіі бібліятэкі DLL, праз некаторы інтэрфейс (гэта значыць, GetName() )
дададзена аўтар Rollie, крыніца
@Photon я прапусціў частка, што destroy_A() бясплатная функцыя.
дададзена аўтар Praetorian, крыніца
Там няма неабходнасці ў гіпсе ў destroy_A() . Деструктор А віртуальнае, таму ~ A_impl() будзе выклікацца, калі выдаліць .
дададзена аўтар Praetorian, крыніца
Праблема з гэтым рашэннем з'яўляецца тое, што структура віртуальных табліц не гарантуецца стандартам. Такім чынам, кліент доступу да вашых віртуальным метадам можа глядзець на віртуальныя табліцы ў іншым месцы, адкуль яна была пабудавана DLL. Гэта можа быць тое, што вы можаце сысці з вялікай часткай часу, але гэта не па-сапраўднаму бяспечныя.
дададзена аўтар James Holderness, крыніца
Адказ на гэтае пытанне, нягледзячы на ​​тое, upvotes, з'яўляецца памылковым. Розныя кампілятары маюць розны АБІС. Яны, як правіла, далучаюцца да інтэрфейсаў тыпу C, але рэдка для класаў C ++.
дададзена аўтар David Heffernan, крыніца
@dyp Так, гэта справядлівае сцвярджэнне
дададзена аўтар David Heffernan, крыніца
@DavidHeffernan Ня сумяшчальнасці ABI для бібліятэк DLL (-> Windows), выкарыстоўваючы толькі ABC (з некаторымі абмежаваннямі) інтэрфейсы амаль як «гарантавана» ў якасці інтэрфейсаў C-тыпу з-за COM падтрымку?
дададзена аўтар dyp, крыніца
Выкажам здагадку, што ў вас ёсць инлайн set_value (Const зЬй :: радок & v) {m_value = v; } Што адбываецца, што ўнутранае поле атрымлівае значэнне, якое выдаткоўваецца выкананым, а не DLL. Гэта проста добрая практыка, каб пазбегнуць падобных праблем у цэлым, а не марнаваць час на адладку, так як некаторыя параметры кампіляцыі/пытанні бібліятэкі выклікаюць дзіўныя эфекты.
дададзена аўтар Photon, крыніца
~ A абаронены, каб забараніць кліенту выдаліць, таму знішчыць функцыя таксама абмежаваная ў гэтым сэнсе.
дададзена аўтар Photon, крыніца
У апошні раз гэтае пытанне турбуе мяне некалькі гадоў таму, у мяне быў DLL з наступным прататыпам для экспартуемых функцый: станд :: струннага FUNC (Const станд :: струннага &). Вяртаецца значэнне было выдзелена ў DLL і вызвалена задзірлівым, выклікаючы збоі. Я не кажу, што вы не можаце сысці з тым, што вы робіце, я проста кажу, што калі вы прапусціце што-то, ён кусае вас у задніцы пазней.
дададзена аўтар Photon, крыніца
Гэта добра ведаць. У вас ёсць альтэрнатыва, якая заўсёды працуе, за выключэннем выкарыстання старога інтэрфейсу C?
дададзена аўтар Photon, крыніца

Калі вы хочаце, каб забяспечыць аб'ектна-арыентаваны інтэрфейс у DLL, якая сапраўды ў бяспецы, я хацеў бы прапанаваць будаваць яго на вяршыні COM аб'ектная мадэль . Гэта тое, што ён быў распрацаваны для.

Любыя іншыя спробы падзяліць класы паміж кодам, які кампілюецца рознымі кампілятарамі мае патэнцыял, каб пацярпець няўдачу. Вы можаце быць у стане атрымаць нешта, што, здаецца, працуе вялікую частку часу, але гэта не можа быць guaraneteed працаваць.

Ёсць верагоднасць, што ў нейкі момант вы будзеце спадзявацца на непрадказальныя паводзіны з пункту гледжання якія заклікаюць канвенцый або класавай структуры або размеркавання памяці.

0
дададзена

Ёсць таксама два «патэнцыйная памылка» (сярод іншых), вы павінны клапаціцца, так як яны звязаны з тым, што знаходзіцца «пад» мовай.

Па-першае, станд :: strng шаблон, і, такім чынам, яно канкрэтызуецца ў кожнай адзінцы трансляцыі. Калі ўсе яны звязаны з адным і тым жа модулем (EXE або DLL) кампаноўнік будзе вырашаць адны і тыя ж функцыі, што і той жа код, і ў канчатковым выніку несумяшчальным кода (той жа функцыі з рознымі цела) разглядаецца як памылка.
Але калі яны звязаны з розным модулем (і EXE і DLL) няма нічога (кампілятар і кампаноўнік) увогуле. Бо на -У модуль, дзе compiled- вы можаце мець розныя рэалізацыі аднаго і таго ж класа з рознымі элементамі і размяшчэнне памяці (напрыклад, адзін можа мець некаторую адладку або прафіляванню дададзеныя функцыі іншы не мае). Доступ да аб'екта, створанаму з аднаго боку, з дапамогай метадаў, сабраных на другім баку, калі ў вас няма іншага спосабу даць паслядоўнасці рэалізацыі, можа скончыцца жаласна.

Другая праблема (больш тонкая) адносіцца да выдзялення/deallocaion памяці: з-за спосабам вокнаў працуе, кожны модуль можа мець асобную кучу. Але стандарт C ++ не вызначае, як новы і Выдаліць клапаціцца пра які кучы аб'ект прыходзіць. І калі радок буфера вылучаецца на адным модулі, чым перайшоў да экземпляра радкі на іншым модулі, вы рызыкуеце (пры разбурэнні), каб даць памяць назад у тую кучу (гэта залежыць ад таго, як новы/выдаліць </код > і Таноса/свабодны рэалізуецца павагай да HeapAlloc/HeapFree :. гэта проста ставіцца да ўзроўню «awarness» рэалізацыя STL ёсць павага да асноўных АСАМ аперацыя сам па сабе не разбуральны аперацыя -The проста fails- але ўцечкі груды аб паходжанні в).

Усё, што сказаў, што гэта не немагчыма , каб перадаць кантэйнер. Гэта залежыць толькі ад вас, каб даць паслядоўнае ажыццяўленне паміж бакамі, так як кампілятар і кампаноўнік не маюць магчымасці пераправерыць.

0
дададзена
Вы не вычэрпваюцца праблемы. Несумяшчальнасць АБІС з'яўляецца забойцам.
дададзена аўтар David Heffernan, крыніца
Затым адрэдагаваць першы сказ, каб быць дакладным.
дададзена аўтар David Heffernan, крыніца
Вядома, але там, дзе ўжо іншыя адказы казаць прымыкаюць яго.
дададзена аўтар Emilio Garavaglia, крыніца
Вы праграміст ці ... кампілятар?
дададзена аўтар Emilio Garavaglia, крыніца

Стандарт C ++ нічога пра ABI, прадстаўленай рэалізацыі не гавораць. Нават на адной платформе змяняецца опцыі кампілятара могуць змяніць двайковыя інтэрфейсы макета або функцыі.

Такім чынам, каб гарантаваць, што стандартныя тыпы могуць быць выкарыстаны праз мяжу DLL гэта ваша адказнасць, каб пераканацца, што альбо:

  • Набыццё рэсурсаў/Release для стандартных тыпаў ажыццяўляецца па той жа DLL. (. Заўвага: вы можаце мець некалькі ЭЛП ў працэсе, але рэсурс набыў crt1.DLL павінен быць вызвалены crt1.DLL )

Гэта не адносіцца да C ++. У C, напрыклад Таноса / бясплатна , Еореп / fclose кожны пары выклікаў неабходна перайсці да аднаго часу выканання C.

Гэта можа быць зроблена або з ніжэй:

  • By explicitly exporting acquisition/release functions ( Photon's answer ). In this case you are forced to use a factory pattern and abstract types.Basically COM or a COM-clone
  • Forcing a group of DLL's to link against the same dynamic CRT. In this case you can safely export any kind of functions/classes.
0
дададзена