I’ve just created an issue on Github to discuss about the addition of #define
to Solidity. I’d love to hear your opinions!
opened 05:15PM - 04 Aug 21 UTC
## Abstract
Add a way to define macros using `#define` within the source code… to declare constants (numbers, strings, addresses, expressions, etc) and fragments of code:
```c
#define PI 3141592653589793238
```
## Motivation
Storing values and performing computations in a smart contract costs gas (and thus money). However when comes the time to gas golf a contract, a dilemma can appear between optimizing the gas usage as much as possible or keeping the readability of the code.
### Example 1
This simplistic example shows that using a direct numeric value for `PI` obviously consumes less gas than storing and reading its value from a constant variable. However too much optimization might impact both the maintainability and the readability of the code.
> Case 1, using PI as a constant
```solidity
contract PIExample {
uint256 public constant PI = 3141592653589793238;
uint256 public area;
uint256 public circumference;
// 44,450 gas used
function setArea(uint256 r) external {
area = PI * r ** 2;
}
// 44,210 gas used
function setCircumference(uint256 r) external {
circumference = 2 * PI * r;
}
}
```
> Case 2, using PI value directly
```solidity
contract PIExample {
uint256 public area;
uint256 public circumference;
// 44,428 gas used
function setArea(uint256 r) external {
area = 3141592653589793238 * r ** 2;
}
// 43,978 gas used
function setCircumference(uint256 r) external {
circumference = 2 * 3141592653589793238 * r;
}
}
```
Programming languages such as C allow developers to define macros within the source code. These macros can represent constants but also fragments of code that can receive arguments. These *defines* are preprocessed by the compiler before compiling the actual code.
Having this feature in Solidity would (in some cases) avoid the necessity to declare constant variables or private functions and then would reduce the gas cost while keeping the readability of the code.
The previous example could then be written as:
```csharp
#define PI 3141592653589793238
contract PIExample {
uint256 public area;
uint256 public circumference;
function setArea(uint256 r) external {
area = PI * r ** 2;
}
function setCircumference(uint256 r) external {
circumference = 2 * PI * r;
}
}
```
### Example 2
This other example compares 3 different cases where two private functions and one constant are used. As expected, using directly the expressions or the values instead of their declarations will reduce the gas cost but also impact the maintainability and readability of the code.
> Case 1: Private functions and PI constant
```solidity
contract PIExample {
uint256 public constant PI = 3141592653589793238;
uint256 public area;
uint256 public circumference;
function calculateArea(uint256 r) private pure returns (uint256) {
return PI * r ** 2;
}
function calculateCircumference(uint256 r) private pure returns (uint256) {
return 2 * PI * r;
}
// 44,493 gas used
function setArea(uint256 r) external {
area = calculateArea(r);
}
// 44,253 gas used
function setCircumference(uint256 r) external {
circumference = calculateCircumference(r);
}
}
```
> Case 2: No private functions, with PI constant
```solidity
contract PIExample {
uint256 public constant PI = 3141592653589793238;
uint256 public area;
uint256 public circumference;
// 44,450 gas used
function setArea(uint256 r) external {
area = PI * r ** 2;
}
// 44,210 gas used
function setCircumference(uint256 r) external {
circumference = 2 * PI * r;
}
}
```
> Case 3: No private functions, no PI constant
```solidity
contract PIExample {
uint256 public area;
uint256 public circumference;
// 44,428 gas used
function setArea(uint256 r) external {
area = 3141592653589793238 * r ** 2;
}
// 43,978 gas used
function setCircumference(uint256 r) external {
circumference = 2 * 3141592653589793238 * r;
}
}
```
Using `#define`, the previous example could be written as:
```csharp
#define PI 3141592653589793238
#define calculateArea(r) (PI * r * r)
#define calculateCircumference(r) (2 * PI * R)
contract PIExample {
uint256 public area;
uint256 public circumference;
// 44,428 gas used
function setArea(uint256 r) external {
area = calculateArea(r);
}
// 43,978 gas used
function setCircumference(uint256 r) external {
circumference = calculateCircumference(r);
}
/**
* Now calculateArea(r) and calculateCircumference(r) can be reused
* multiple times in different functions with no additional gas cost
*/
}
```
Obviously these 2 examples are depicting very basic use cases of the `#define` macro, and even if the improvement of both the gas cost and the readability might not be obviously here, the impact on large-scale projects might be significant.
Additionally, these macros could also be used to:
- Reduce the storage of some math libraries by replacing constant variables
- Reduce the gas cost of large projects by replacing private functions
- Etc
*Note: A suggested way to achieve this feature without modifying the Solidity language was to use an external preprocessing tool, such as [hardhat-preprocessor](https://hardhat.org/plugins/hardhat-preprocessor.html). However, relying on an external tool is not an option since it would result in writing non-standard Solidity code and would break the compatibility with many tools.*
## Specification
The specification of the `#define` macro would be very similar to the one used in the C programming language.
### Simple macro
A simple macro is a fragment of code defined by a name using `#define`:
```c
#define PI 31415
#define SOME_ADDRESS 0x012345...
```
Every time the compiler would encounter `PI` in the code, it would replace it by `31415`, etc.
### Function-like macro
More complex macros could be defined too: function-like macros. These special macros would work like a function call and would accept arguments.
```c
#define PI 31415
#define circleArea(r) (PI * r * r)
#define max(a, b) (a < b) ? b : a
```
The compiler would then replace `circleArea(r)` by `(31415 * r * r)`, etc.
### Multiline macro
Macros could also be defined on multiple lines, such as:
```c
#define PI\
3141592653589793238
```
### Inheritance
Macros should be available in the child contracts.
## Backwards Compatibility
The addition of this feature would not impact the compatibility with previous Solidity version.
I think your feature request might be a duplicate of issue #10 which is currently the oldest open issue. I think that this should ultimately not be added to the language itself but instead we should have a recommended preprocessor.
There are actually 4 issues where there was some discussion about this on GitHub:
Add #define to Solidity
Feature request: MACRO
Feature request: macros
Solidity feature request: macros
I made a tally of what people’s stances are in all four of these issues.
xinbenlv: yes
clemlak: yes
hrkrshnn: maybe no
sambacha: neutral
banshee: maybe yes
axic: no
nielsbishere: yes
fulldecent: no
rellfy: maybe yes
mikebolt: no
wjmelements: yes
lianahus: neutral
nmushegian: yes
debris: maybe yes
chriseth: no
LefterisJP: yes
zitterbewegung: maybe yes
flockonus: yes
marenz: no
cameel: neutral
Totals
yes: 7
maybe yes: 3
neutral: 3
maybe no: 1
no: 5
Feel free to correct the record, but based on this it seems like the majority of people want macros.