Могу ли я добавить числа с препроцессором C/C++?

Для какой-то базы. База 1 четная. Какая-то сложная замена-замещение.

Кроме того, и, конечно, делать это не очень хорошая идея в реальном производственном коде. Я просто спросил из любопытства.

8 ответов

Решение

Вы можете относительно легко написать макрос, который добавляет два целых числа в двоичном виде. Например - макрос, который суммирует два 4-битных целых числа в двоичном виде:

#include "stdio.h"

// XOR truth table
#define XOR_0_0 0
#define XOR_0_1 1
#define XOR_1_0 1
#define XOR_1_1 0

// OR truth table
#define OR_0_0 0
#define OR_0_1 1
#define OR_1_0 1
#define OR_1_1 1

// AND truth table
#define AND_0_0 0
#define AND_0_1 0
#define AND_1_0 0
#define AND_1_1 1

// concatenation macros
#define XOR_X(x,y) XOR_##x##_##y
#define   OR_X(x,y) OR_##x##_##y
#define  AND_X(x,y) AND_##x##_##y
#define OVERFLOW_X(rc1,rc2,rc3,rc4, rb1,rb2,rb3,rb4, a1,a2,a3,a4, b1,b2,b3,b4) OVERFLOW_##rc1 (rc1,rc2,rc3,rc4, rb1,rb2,rb3,rb4, a1,a2,a3,a4, b1,b2,b3,b4)

// stringification macros
#define STR_X(x) #x
#define STR(x) STR_X(x)

// boolean operators
#define XOR(x,y) XOR_X(x,y)
#define   OR(x,y) OR_X(x,y)
#define  AND(x,y) AND_X(x,y)

// carry_bit + bit1 + bit2
#define BIT_SUM(carry,bit1,bit2) XOR(carry, XOR(bit1,bit2))
// carry_bit + carry_bit_of(bit1 + bit2)
#define CARRY_SUM(carry,bit1,bit2) OR(carry, AND(bit1,bit2))

// do we have overflow or maybe result perfectly fits into 4 bits ?
#define OVERFLOW_0(rc1,rc2,rc3,rc4, rb1,rb2,rb3,rb4, a1,a2,a3,a4, b1,b2,b3,b4) SHOW_RESULT(rc1,rc2,rc3,rc4, rb1,rb2,rb3,rb4, a1,a2,a3,a4, b1,b2,b3,b4)
#define OVERFLOW_1(rc1,rc2,rc3,rc4, rb1,rb2,rb3,rb4, a1,a2,a3,a4, b1,b2,b3,b4) SHOW_OVERFLOW(rc1,rc2,rc3,rc4, rb1,rb2,rb3,rb4, a1,a2,a3,a4, b1,b2,b3,b4)

// draft-horse macros which performs addition of two 4-bit integers
#define ADD_BIN_NUM(a1,a2,a3,a4, b1,b2,b3,b4) ADD_BIN_NUM_4(0,0,0,0, 0,0,0,0, a1,a2,a3,a4, b1,b2,b3,b4)
#define ADD_BIN_NUM_4(rc1,rc2,rc3,rc4, rb1,rb2,rb3,rb4, a1,a2,a3,a4, b1,b2,b3,b4) ADD_BIN_NUM_3(rc1,rc2,rc3,AND(CARRY_SUM(0,a4,b4),OR(a4,b4)), rb1,rb2,rb3,BIT_SUM(0,a4,b4), a1,a2,a3,a4, b1,b2,b3,b4)
#define ADD_BIN_NUM_3(rc1,rc2,rc3,rc4, rb1,rb2,rb3,rb4, a1,a2,a3,a4, b1,b2,b3,b4) ADD_BIN_NUM_2(rc1,rc2,AND(CARRY_SUM(rc4,a3,b3),OR(a3,b3)),rc4, rb1,rb2,BIT_SUM(rc4,a3,b3),rb4, a1,a2,a3,a4, b1,b2,b3,b4)
#define ADD_BIN_NUM_2(rc1,rc2,rc3,rc4, rb1,rb2,rb3,rb4, a1,a2,a3,a4, b1,b2,b3,b4) ADD_BIN_NUM_1(rc1,AND(CARRY_SUM(rc3,a2,b2),OR(a2,b2)),rc3,rc4, rb1,BIT_SUM(rc3,a2,b2),rb3,rb4, a1,a2,a3,a4, b1,b2,b3,b4)
#define ADD_BIN_NUM_1(rc1,rc2,rc3,rc4, rb1,rb2,rb3,rb4, a1,a2,a3,a4, b1,b2,b3,b4)      OVERFLOW(AND(CARRY_SUM(rc2,a1,b1),OR(a1,b1)),rc2,rc3,rc4, BIT_SUM(rc2,a1,b1),rb2,rb3,rb4, a1,a2,a3,a4, b1,b2,b3,b4)
#define OVERFLOW(rc1,rc2,rc3,rc4, rb1,rb2,rb3,rb4, a1,a2,a3,a4, b1,b2,b3,b4) OVERFLOW_X(rc1,rc2,rc3,rc4, rb1,rb2,rb3,rb4, a1,a2,a3,a4, b1,b2,b3,b4)
#define   SHOW_RESULT(rc1,rc2,rc3,rc4, rb1,rb2,rb3,rb4, a1,a2,a3,a4, b1,b2,b3,b4) STR(a1) STR(a2) STR(a3) STR(a4) " + " STR(b1) STR(b2) STR(b3) STR(b4) " = " STR(rb1) STR(rb2) STR(rb3) STR(rb4)
#define   SHOW_OVERFLOW(rc1,rc2,rc3,rc4, rb1,rb2,rb3,rb4, a1,a2,a3,a4, b1,b2,b3,b4) STR(a1) STR(a2) STR(a3) STR(a4) " + " STR(b1) STR(b2) STR(b3) STR(b4) " = overflow"

void main()
{
    printf("%s\n", 
        ADD_BIN_NUM(
                    0,0,0,1, // first  4-bit int
                    1,0,1,1) // second 4-bit int
                    );

    printf("%s\n", 
        ADD_BIN_NUM(
                    0,1,0,0, // first  4-bit int
                    0,1,0,1) // second 4-bit int
                );

    printf("%s\n", 
        ADD_BIN_NUM(
                    1,0,1,1, // first  4-bit int
                    0,1,1,0) // second 4-bit int
                );
}

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

РЕДАКТИРОВАТЬ: я изменил форматирование результатов и, что более важно, я добавил проверку переполнения.

НТН!

Препроцессор работает с токенами предварительной обработки, и единственный раз, когда он оценивает числа, это во время оценки #if или же #elif директивы. Кроме того, числа не являются действительно числами во время предварительной обработки; они классифицируются как токены предварительной обработки числа, которые на самом деле не являются числами.

Вы можете оценить базовую арифметику с помощью конкатенации токенов:

#define ADD_0_0 0
#define ADD_0_1 1
#define ADD_1_0 1
#define ADD_1_1 2

#define ADD(x, y) ADD##_##x##_##y

ADD(1, 0) // expands to 1
ADD(1, 1) // expands to 2

На самом деле, однако, нет никаких причин делать это, и было бы глупо делать это (вам нужно было бы определить огромное количество макросов, чтобы оно было хотя бы удаленно полезным).

Было бы разумнее иметь макрос, который расширяется до целочисленного константного выражения, которое может быть оценено компилятором:

#define ADD(x, y) ((x) + (y))

ADD(1, 1) // expands to ((1) + (1))

Компилятор сможет оценить 1 + 1 выражение.

Вполне возможно сделать ограниченное целочисленное сложение в препроцессоре. И это на самом деле нужно чаще, чем можно было бы надеяться, то есть альтернатива просто иметь ((2) + (3)) в программе не работает. (Например, вы не можете иметь переменную с именем x((2)+(3))). Идея проста: превратить дополнение в приращения, которые вы не возражаете (слишком много), перечислив их все. Например,

#define INC(x) INC_ ## x
#define INC_0 1
#define INC_1 2
#define INC_2 3
#define INC_3 4
#define INC_4 5
#define INC_5 6
#define INC_6 7
#define INC_7 8
#define INC_8 9
#define INC_9 10
INC(7) // => 8

Теперь мы знаем, как сделать дополнение до 1.

#define ADD(x, y) ADD_ ## x(y)
#define ADD_0(x) x
#define ADD_1(x) INC(x)
ADD(0, 2) // => 2
ADD(1, 2) // => 3

Чтобы добавить еще большие числа, вам нужна какая-то "рекурсия".

#define ADD_2(x) ADD_1(INC(x))
#define ADD_3(x) ADD_2(INC(x))
#define ADD_4(x) ADD_3(INC(x))
#define ADD_5(x) ADD_4(INC(x))
#define ADD_6(x) ADD_5(INC(x))
#define ADD_7(x) ADD_6(INC(x))
#define ADD_8(x) ADD_7(INC(x))
#define ADD_9(x) ADD_8(INC(x))
#define ADD_10(x) ADD_9(INC(x))
ADD(5, 2) // => 7

Однако в этом следует быть осторожным. Например, следующее не работает.

#define ADD_2(x) INC(ADD_1(x))
ADD(2, 2) // => INC_ADD_1(2)

Для любого расширенного использования таких уловок, Boost Preprocessor - ваш друг.

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

#include <iostream>
using namespace std;

template <int N, int M>
struct Add
{
    static const int Value = N + M;
};

int main()
{
    cout << Add<4, 5>::Value << endl;
    return 0;
}

Видимо, вы можете. Если вы посмотрите на библиотеку Boost Preprocessor, вы можете делать все с помощью препроцессора, даже целочисленные.

Макросы препроцессора на самом деле не могут выполнять арифметику, но их можно эффективно использовать для математических операций с перечислениями. Основная хитрость заключается в том, чтобы иметь макрос, который вызывает другие макросы, и его можно повторно вызывать, используя различные определения этих других макросов.

Например, что-то вроде:

#define MY_THINGS \
  что-то (FRED,4) \
  что-то (GEORGE,6) \
  что-то (Гарри,5) \
  что-то (ГЕРМИОНА,8) \
  что-то (RON,3) \
  // Эта строка оставлена ​​пустой 

#define a_thing(name,size) EN_##name}; enum {EN_SIZE_##name=(size),EN_BLAH_##name = EN_##name+(size-1),
enum {EN_FIRST_THING=0, MY_THINGS EN_TOTAL_SIZE};
#undef a_thing

Это позволит "выделить" определенное количество места для каждой вещи, например, в массиве. Математика не выполнена препроцессором, но перечисления все еще рассматриваются как константы времени компиляции.

Препроцессор C может оценивать условия, содержащие целочисленную арифметику. Он не будет заменять арифметические выражения и передавать результат компилятору, но компилятор будет оценивать арифметику констант времени компиляции и выводить результат в двоичный файл, если вы не перегружаете используемые операторы.

Я уверен, что препроцессор C/C++ просто копирует и вставляет - он фактически не оценивает выражения. Оценка выражения выполняется компилятором.

Чтобы лучше ответить на ваш вопрос, вы можете опубликовать то, что вы пытаетесь достичь.

Другие вопросы по тегам