C++ как препроцессор в C

rosali

Несколько раз, в том числе и здесь, кажется, слышал мысль, что C++ это всего лишь препроцессор в C. По началу поверил:

  • this - это всего лишь обычный параметр
  • перегруженные операторы, конструктор/деструктор - обычные функции
  • _vtable - struct из указателей на функции
  • template-ы - всего лишь язык макр, он даже по логике С++ просто препроцессируется
  • Передача объектов параметров - это всего лишь соответствующие вызовы copy-constructor-ов

Но вот сейчас задумался, а на что в С можно отобразить exception handling? Ведь процесс размотки стека, вроде бы, на С невыразим, а он должен быть сгенерирован в соответствии с тем, как этот стек создается. Есть какие-нибудь идеи на тему?

sergey_m

А компилятор (если без -O) это препроцессор в ассемблер?

rosali

Да.
Когда я говорю "С++ - это препроцессор в С" - это значит, что в принципе можно написать программу c++2c.exe и результат ее работы компилировать любым С-компилятором.

shlyumper

При таком подходе вообще не существует компиляторов. Можно написать программу c2asm.exe, которая будет переводить все в ассемблер (причем даже без специфики типа MS/Turbo, а под любой). Это тоже будет препроцессор?

rosali

Одни языки уступают по выразительности другим. Ассемблер выразительнее всех, но не С. Ведь не все, что можно написать на ассемблере, можно написать и на С (можно пообсуждать). Моя гипотеза, что С++ выразительнее чем С (снова, можно пообсуждать и поэтому преобразователь в С невозможен...
PS Слова компилятор, препроцессор, конвертор, ... я, как-то, не очень различаю

Dasar

> на что в С можно отобразить exception handling?
следующая программа:


class MyClass {public: MyClass{} ~MyClass{}};
void FirstFunc
{
какой-то код
try
{
MyClass c1;
какой-то код2;
throw 1;
какой-то код3;
}
catch (...)
{
}
}


преобразуется в следующий c-ишный код:


void FirstFunc
{
какой-то код;
запомним состояние стека (q1);
вызвать функцию MyClass::MyClass;
запомнить, что необходимо вызвать функцию MyClass::~MyClass;
какой-то код2;

//throw 1;
востановить состояние стека q1, вызвав все запомненные функции, которые были после этого состояния
}

rid2000

ИМХО: Объектно-ориентированный подход - это имитация, для упрощения проектирования систем (С++)...
ЗЫ: Кстати, про Java на уровне JVM - тут ... хм...

rosali

запомним состояние стека (q1);

Радость exception-ов в С++ как раз в том и состоит, что если исключения не происходит, то накладных расходов нет вообще. Этим то они и лучше, чем бесконечные С-шные


if( (ret = f != 0 )
return(ret);


не говоря уж о простоте программирования.
А в том, что на С можно написать интерпретатор обработки исключений, я не сомневаюсь.

kokoc88

При хорошей реализации накладные расходы будут минимальны, а то и вовсе равны.

Dasar

> если исключения не происходит, то накладных расходов нет вообще
гон, посмотри дизассемблер
там есть инструкция типа mov fs:xx, чего-то там
это и есть сохранение состояния, а также запись о том, какие деструкторы необходимо будет вызвать
ps
информация о том, какие деструкторы в каком момент надо вызвать, подготавливается на этапе компиляции, в программе на C - это также можно сделать заранее.

rosali

там есть инструкция типа mov fs:xx, чего-то там

Вот западло! и впрямь есть... Я думал это только при отладке, чтобы debugger мог раньше моей программы исключения ловить, а нет, даже под release-ом это есть... В gcc правда всего один movl, но все равно есть:


void __attribute__noinline h(void)
{
throw E;
}
void __attribute__noinline f(void)
{
A a;
h;
}
......
.globl _Z1fv
.type _Z1fv, @function
_Z1fv:
.LFB1633:
pushl %ebp
.LCFI6:
movl %esp, %ebp
.LCFI7:
subl $40, %esp
.LCFI8:
movl %ebx, -4(%ebp) ; ВОТ ЭТОТ
.LCFI9:
.LEHB0:
call _Z1hv
......

rosali

Странно вообще-то... А почему этот __cxa_throw или как его там не может, глядя на стек, сообразить, в каких обстоятельствах его вызвали, без посторонней помощи. Он правда для этого должен быть не библиотечным, а генерироваться с программой вместе. Я всегда думал, что так и делается...

mirt1971

C++ действительно можно реализовать как препроцессор. Такая штука даже есть(была?) - сfront называется. А exceptions можно реализовать с помощью setjmp/longjmp. Зная стэк до и после exception(те longjmp) можно пройтись по нужной части стэка и вызвать деструкторы). Правда все это довольно медленно. Поэтому создатели cfront в итоге отказались от такого подхода к компилятору.

maggi14

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

rosali

и все, что может сделать процессор, можно представить на Си.

Смешно! То что все процессоры, которые ты видел, так сказать, С-совместимы - это следствие лишь популярности С, а не его универсальности. А если в процессоре вообще нет байтов, как его на С программировать? А про Data Flow архитектуры ты слышал? Да просто мультитредовый процессор даже на С программируется не естественно, через какие-нибудь POSIX Threads?...

maggi14

Не учите меня жить И не придирайтесь к словам. Я сам программирую DSP на интеловских телефонных платах, и, естественно, знаю, что есть и сильно другие архитектуры. Си и Си++ предназначены для одних и тех же процов.

rosali

setjmp/longjmp... Правда все это довольно медленно...

Это немного не то. Связка C -> Asm -> Binary транзитивна, то есть результат получится одинаковый, если компилировать поэтапно или сразу. И можно было бы расчитывать на то, что C++ -> C -> Binary тоже будет обладать таким свойством (если конечно оптимизирующие блоки у С++ и у С компиляторов одинаковые). Или ты имеешь в виду "Правда все это довольно медленно [компилируется]"?

mirt1971

Я имел ввиду "медленно работает". Вообще говоря exception handling реально нельзя сделать быстро(как препроцессор). Кстати говоря ранние gcc использовали именно longjmp/setjmp.
Оставить комментарий
Имя или ник:
Комментарий: