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

Для какой-то базы. База 1 четная. Какая-то сложная подстановка -ing.

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

Ответы [ 8 ]

12 голосов
/ 22 августа 2010

Препроцессор работает с токенами предварительной обработки, и единственный раз, когда он оценивает числа, это во время оценки директивы #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выражение.

10 голосов
/ 23 сентября 2011

Вы можете относительно легко написать макрос, который добавляет два целых числа в двоичный файл . Например - макрос, который суммирует два 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-битных целых. В общем, все, что нам нужно, - это правила объединения и замены токенов для достижения потрясающих результатов с помощью макросов.

EDIT: Я изменил форматирование результатов и, что более важно, я добавил проверку переполнения.

НТН!

5 голосов
/ 10 мая 2013

Вполне возможно сделать ограниченное целочисленное сложение в препроцессоре. И это на самом деле нужно чаще, чем можно было бы надеяться, т. Е. Альтернатива просто иметь ((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 - ваш друг.

5 голосов
/ 22 августа 2010

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

#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;
}
4 голосов
/ 23 сентября 2011

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

1 голос
/ 22 августа 2010

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

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

#define MY_THINGS \
  a_thing(FRED,4) \
  a_thing(GEORGE,6) \
  a_thing(HARRY,5) \
  a_thing(HERMIONE,8) \
  a_thing(RON,3) \
  // This line left blank 

#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

Это позволитодин для «выделения» определенного количества места для каждой вещи, например, в массиве.Математика не выполняется препроцессором, но перечисления по-прежнему считаются константами времени компиляции.

1 голос
/ 22 августа 2010

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

0 голосов
/ 22 августа 2010

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...