Программирование >>  Полиморфизм без виртуальных функций в с++ 

1 ... 73 74 75 [ 76 ] 77 78 79 ... 144


void g() {

f(l); связывается с f Fi

f(l, asdf ); связывается с f FiPc

f(l,l); попытка связать с f Fdd

ошибка во время компоновки:

f Fdd не определена

Это оставляет открытым вопрос о том, как вызвать С- или С++-функции. Для этого программист должен указать, что функция имеет С-компоновку. В противном случае предполагается, что это C-+-фyнкция, и ее имя кодируется. С этой целью в С++ была введена спецификация компоновки:

extern С (

double sqrt(double); sqrt(double) имеет С-компоновку

Спецификация компоновки не затрагивает семантику программы, в которой используется sqrt (), а лишь дает ко.\шилятору инфор.мацию, что при генерировании объектного кода он должен использовать для sqrt () принятые в С соглашения об именах. Это означает, что имя данного экземпляра sqrt () будет равно sqrt или sqrt, или иному варианту, в зависимости от конкретной системы. Можно представить себе систему, в которой для С приняты правила типобезопасной компоновки из С++, так что видимое компоновщику имя функции sqrt () будет sqrt Fd.

Естественно, кодирование типа с помощью добавления суффикса - лишь один из возможных способов реализации, но он был успешно применен в Cfront, а затем широко растиражирован. У этого способа есть важные свойства; простота и совместимость с существующими компоновщика.ми. Такая реализация идеи типобезопасной компоновки не является абсолютно безопасной, но ведь в любом случае лишь очень немногие из полезных систем безопасны на 100%. Более полное описание схе.мы кодирования и.мен, примененной в Cfront, приводится в [ARM, §7.2с].

11.3.3. Анализ пройденного пути

Видимо, мы правильно определили приоритеты: типобезопасная компоновка, на/тчие разумной реализации и возможность явной компоновки с програ,ммами, написанны.ми на других языках. Как и ожидалось, с по.мощью новой систе.мы компоновки решался целый ряд проблем. Помимо всего прочего, в процессе перехода к новому стилю обнаружилось на удивление большое число ошибок при компоновке старых программ, написанных на С и С++. Я тогда отметил: При переходе на типобезопасную ко.мпоновку чувствуеп]ь себя, как после первой проверки С-программы с помошью lint, - несколько обескураженно . Lint - это популярный инструмент проверки раздельно компилируемых частей С-программы на предмет непротиворечивого использования типов [Kernighan, 1984]. Во время опытной эксплуатации типобезопасной компоновки я пытался отслеживать результаты ее



работы. С помощью данного средства выявлялись необнаруженные ошибки в каждой из больших програ.мм на С и C+-I-, которые мы пытались откомпилировать и связать.

К нашему удивлению, некоторые профаммисты сознательно вносили ошибки в объявления функций, просто чтобы подавить сообщения об ошибках. Например, вызов f (1, а) приводит к ошибке, если f () не объявлена. Я наивно ожидал, что в таком случае профаммист либо добавит правильное объявление функции, либо включит его в заголовочный файл. Оказалось, что была даже третья возможность - просто по.местить любое объявление, не противоречащее вызову:

void g() {

void f(int ...); чтобы подавить сообщение об ошибке ... f(1,а);

Типобезопасная компоновка выдает сообщение об ошибке, если объявление не соответствует определению.

Также была обнаружена проблема переносимости. Многие объявляли библиотечные функции пря.мо в коде, вместо того чтобы включить нужный заголовочный файл. Полагаю, что это делалось для уменьшения времени компиляции, но в результате при переносе на другую систе.му объявление оказывалось неверным. Типобезопасная компоновка позволила нам выявить целый ряд таких проблем переносимости (в основном между UNIX System V и BSD UNIX).

Прежде чем остановиться на той схеме, которая включена в язык, был рассмотрен ряд других возможностей [Stroustrup, 1988]:

□ не вводить явных директив компоновки, а положиться на инстру.менталь-ные средства при связывании с С-функциями;

□ выполнять типобезопасную компоновку и перегрузку только для функций, явно помеченных ключевым словом overload;

□ осуществлять типобезопасную компоновку только для функций, которые никак не могли быть С-функциями, поскольку их типы нельзя выразить на С.

Опыт использования принятой схемы убедил меня в том, что проблемы, которых я опасался в случае выбора альтернативного решения, были вполне реальными. Например, распространение контроля на все функции стало благом, а программирование на смеси С и C-t-+ оказалось настолько популярным, что любое усложнение совместной компоновки было бы воспринято болезненно.

Две особенности вызывали со стороны пользователей нарекания, которые не утихли до сих пор. В первом случае я считаю, что мы были правы, а относительно второго не уверен.

Функция, объявленная как имеющая С-компоновку, по-прежнему обладает семантикой вызова, принятой в С++. Это значит, что формальные аргументы должны быть объявлены, а фактические - соответствовать им с учетом правил сопоставления и разрешения неоднозначности, действующих в С++. Некоторые пользователи хотели бы, чтобы функции с С-ко.мпоновкой подчинялись правилам



вызова С. В таком случае можно было упростить использование заголовочных файлов С. Но это же позволило бы небрежным программистам вернуться к ослабленному контролю типов, характерному для С. Еще один аргумент против специальных правил для С связан с тем, что другие программисты высказывали такие же просьбы для компоновки с Pascal, Fortran и PL/I с поддержкой соответствующих правил вызова. Напри.мер, для функций с Pascal-компоновкой предлагалось неявно преобразовывать С-строки в Pascal-строки; для функций с Fortran-компоновкой - реализовать вызов по ссылке и добавлять информацию о типе массива и т.д. Если бы мы предоставили специальные возможности для С, то были бы обязаны наделить компилятор С-ы- знанием соглашений о вызове, принятых в огромном количестве языков. Было правильным воспротивиться такому давлению, хотя включение подобных дополнительных услуг и помогло бы отдсльны.м программистам, работающим на смеси языков. Располагая только лишь семантикой С++, многие обнаружили, что для построения интерфейсов с таки.ми языками, как Pascal и Fortran, где поддерживается передача аргументов по ссылке, полезны ссылки С++ (см. раздел 3.7).

С другой стороны, акцентирование внимания только на компоновке породило определенную сложность. В нашем решении прямо не рассматривались проблемы среды, поддерживающей программирование на смеси языков, и указатели на функции с разны.ми соглашениями о вызове. Пользуясь правилами ко.мнонов-ки С++, мы можем непосредственно выразить, каки.м соглашениям о вызове подчиняется написанная на С или С++ функция. Но нельзя просто сказать, что са.ма функция подчиняется соглашениям С++, а ее аргументы - соглашениям С. Можно выразить это косвенно [ARM, стр. 118], например:

typedef void (*PV)(void*,void*);

void* sortKvoid*, unsigned, PV) ;

extern C void* sort2(void*, unsigned, PV);

Здесь sortl 0 имеет С++-компоновку, принимает указатель на функцию с С++-К0МП0Н0ВК0Й; sort2 () имеет С-компоновку, принимает указатель на функцию с С++-К0МП0Н0ВК0Й. Это простые случаи. Другой пример:

extern С typedef void (*CPV)(void*,void*);

void* sort3(void*, unsigned, CPV);

extern C void* sort4(void*, unsigned, CPV);

Здесь sorts () имеет С++-компоновку и принимает указатель на функцию с С-ко.мпоновкой; sort 4 () имеет С-компоновку и принимает указатель на функцию с С-компоновкой. Это почти предел того, что можно выразить в языке. Альтернативы тоже не очень удачны: можно либо ввести соглашения о вызове в систему типов, либо использовать при вызове переходники для преобразования одних соглашений в другие.

Компоновка, межъязыковые вызовы и передача объектов из одного языка в другой - это непростые проблемы, у них много аспектов, зависящих от реализации. В данной области правила меняются по мере возникновения новых языков, аппаратных архитектур и методов реализации.



1 ... 73 74 75 [ 76 ] 77 78 79 ... 144

© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки.
Яндекс.Метрика