Бьерн Страуструп - Язык программирования С++. Главы 2-4
Страница 55. Связывание с программами на других языках


 

4.4 Связывание с программами на других языках

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

             extern "C" char* strcpy(char*, const char*);

Результат этого описания отличается от результата обычного описания

             extern char* strcpy(char*, const char*);

только порядком связывания для вызывающих strcpy() функций. Сама
семантика вызова и, в частности, контроль фактических параметров
будут одинаковы в обоих случаях. Описание extern "C" имеет смысл
использовать еще и потому, что языки С и С++, как и их
реализации, близки друг другу. Отметим, что в описании extern "C"
упоминание С относится к порядку связывания, а не к языку, и часто
такое описание используют для связи с Фортраном или ассемблером.
Эти языки в определенной степени подчиняются порядку связывания
для С.
    Утомительно добавлять "C" ко многим описаниям внешних, и
есть возможность указать такую спецификацию сразу для группы
описаний. Например:

              extern "C" {
                 char* strcpy(char*, const char);
                 int strcmp(const char*, const char*)
                 int strlen(const char*)
                 // ...
              }

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

              extern "C" {
                 #include <string.h>
              }

 Обычно с помощью такого приема из стандартного заголовочного файла
 для С получают такой файл для С++. Возможно иное решение с
 помощью условной трансляции:

              #ifdef __cplusplus
              extern "C" {
              #endif

                  char* strcpy(char*, const char*);
                  int strcmp(const char*, const char*);
                  int strlen(const char*);
                  // ...

               #ifdef __cplusplus
               }
               #endif

 Предопределенное макроопределение __cplusplus нужно, чтобы обойти
 конструкцию extern "C" { ...}, если заголовочный файл используется
 для С.
      Поскольку конструкция extern "C" { ... } влияет только на
 порядок связывания, в ней может содержаться любое описание,
 например:

              extern "C" {
                // произвольные описания

                // например:

                static int st;
                int glob;
              }

 Никак не меняется класс памяти и область видимости
 описываемых объектов, поэтому по-прежнему st подчиняется внутреннему
 связыванию, а glob остается глобальной переменной.
      Укажем еще раз, что описание extern "C" влияет только на
 порядок связывания и не влияет на порядок вызова функции. В частности,
 функция, описанная как extern "C", все равно подчиняется правилам
 контроля типов и преобразования фактических параметров, которые в C++
 строже, чем в С. Например:

             extern "C" int f();

             int g()
             {
               return f(1);  // ошибка: параметров быть не должно
             }

 
« Предыдущая статья   Следующая статья »