Ad rem
C-- является абстракцией аппаратного обеспечения, вследствие чего "разговор" здесь ведется сугубо на низком уровне, а никак не на высоком. Железо обеспечивает вычисления, управление потоками, памятью и регистрами - C--, со своей стороны, "обещает" предоставить соответствующие абстракции.
Выражения и присваивания C-- служат абстракциями вычислений. Язык оснащен богатым комплексом вычислительных операторов, но работают они лишь с типами данных машинного уровня: байтами, словами ит.п. Абстракция выражения скрывает конкретную комбинацию машинных команд, требуемых для вычисления значений, а также машинные регистры, которые необходимы для хранения промежуточных результатов.
Управляющие конструкции goto (по отношению к C-- стоит пересмотреть свои убеждения, продиктованные чистой верой в непогрешимость Великого Эдсгера В. Дэйкстры (Edsger W. Dijkstra) насчет вреда данного оператора) и if - абстракции потока управления. Последняя скрывает машинные "коды условий"; расширенные условия представляют собой произвольные булевы (логические) выражения.
Обращение к памяти в языке-гибриде подобно таковому на машинном уровне - за исключением того лишь, что адреса, используемые в программах C--, могут быть произвольными выражениями. Эта абстракция скрывает ограничения режимов машинной адресации.
Переменные C-- выступают в роли абстракций регистров. C-- пытается, насколько это возможно, хранить все переменные в регистрах CPU, если же они туда не помещаются (ввиду превышения лимита дозволенного объема), то идут в основную память. Абстракция скрывает количество и метод использования стандартных регистров вычислительной системы.
Дополнительно в C-- присутствует также процедурная абстракция - характеристика эта выглядит как аппаратный примитив. Тем не менее, много процессорных архитектур представляют прямую поддержку для процедур, хотя сама природа этой поддержки широко варьируется (процедурный вызов или многочисленные команды сохранения данных в регистрах, регистровые окна, регистры связи, расширенные предсказания для команд возврата и т.п.). По причине такого разнообразия соглашения о вызовах и управлении стек-активизацией являются сильно архитектуро-зависимыми и трудно определяемыми.
Клиент C-- состоит из двух частей: компилятора с языка высокого уровня - front-end, и перенастраиваемого (retargetable) генератора кода - back-end. Предполагается, что исполняемая программа разделена на три составляющие, каждая из которых может размещаться в объектных файлах, библиотеках или их комбинациях.
Компилятор front-end транслирует высокоуровневый исходный текст программы в один или более модулей C--, которые после сепаратно транслируются компилятором C-- в генерируемый объектный код. Front-end входит в front-end run-time систему, включающую сборщик мусора, обработчик исключений и другие средства, которые необходимы исходному языку. Сама программа пишется на языке программирования, предназначенном для людей, а не на C--.
С каждой реализацией C-- поставляется run-time система; основная цель системы - поддержка и обеспечение доступа к информации, которая может быть известна только генератору кода. Она делает доступной эту информацию для front-end run-time системы посредством C-- run-time интерфейса. Различные front-ends могут взаимодействовать с одной C-- run-time системой. Для формирования исполняемой программы сгенерированный объектный код связывается с обеими run-time системами.
В общих чертах, C-- может поддерживать высокоуровневые run-time сервисы, к примеру сборки мусора, следующим образом. Сначала управление передается front-end run-time системе. Сборщик мусора проходит стек C--, вызывая программы доступа (процедуры) из run-time системы языка; в каждой активизационной записи стека он, используя дальнейшие процедуры (представляемые C-- run-time), находит размещение каждой переменной. (Хотя следует отметить, что C-- run-time не известно, в какой из переменных хранится указатель.)
Front-end компилятор формирует статически распределяемый блок данных, идентифицирующий переменные указателей, и использует директиву span, чтобы ассоциировать этот блок данных с соответствующей областью процедуры программных счетчиков. Сборщик мусора совмещает эти два источника информации для выбора процедурной переменной как корня.
Используя run-time интерфейс, front-end run-time система может проверять и модифицировать состояние приостановленных вычислений. Вместо определения представительств приостановленных вычислений или активизационных записей, они скрыты за простыми абстракциями. Эти абстракции присутствуют в front-end run-time системе в виде набора C-процедур (подпрограмм, написанных на языке C).
Камнем преткновения для разработчиков языка стала легкость в перенастройке front-ends, а не в возможности запуска всех программ на C-- везде. Несмотря на то, что любая C-- программа имеет, независимо от машины, отчетливо выраженную семантику, высокоуровневому компилятору, транслирующему исходную программу для двух целевых архитектур, необходимо сгенерировать две разные C-- программы. Например, программа, сгенерированная для машины с командами с плавающей запятой, будет отличаться от программы, сгенерированной для машины без плавающей запятой.