This is the multi-page printable view of this section. Click here to print...
Functions
- 1: System Functions
- 1.1: millis()
- 1.2: delay()
- 1.3: delayMicroseconds()
- 1.4: random()
- 2: General Purpose Digital IO
- 2.1: pinMode()
- 2.2: digitalWrite()
- 2.3: digitalRead()
- 2.4: attachIntDio()
- 2.5: detachIntDio()
- 2.6: digitalReadBitmap()
- 3: Utility Functions
- 3.1: Printf Implementation
- 3.2: pack_bits()
- 3.3: collect_bits()
- 3.4: Byte array utils
- 3.5: pack_bytes()
- 3.6: expand-bytes()
- 3.7: CRC8, XOR, LRC
- 3.8: div10(), div100(), div1000()
- 3.9: Scale utils
- 3.10: pnew()
1 - System Functions
1.1 - millis()
uint32_t millis()
The system time is updated by TickTimer interrupts.
1.2 - delay()
void delay(uint32_t ms)
Performs a delay for the period specified by ms
.
Time measurement is done using the TickTimer count. When a long delay is specified, the CPU clock is reduced during the polling process.
Every approximately 5 ms after calling delay()
, the internal watchdog process of the TWELITE microcontroller is executed.
※ For example, in the case of while(1) delay(1);
, the delay()
function does not exceed 5 ms, so the watchdog process is not invoked, resulting in a reset after a certain period.
Within the setup()
and wakeup()
functions, since the TickTimer is not yet running, delays are implemented using a while-loop. In this case, the discrepancy from the specified value may be significant. The loop counter is calibrated for 32 MHz. If the CPU clock is altered within these functions, errors proportional to that clock change will occur.
If a short delay such as 1 or 2 ms is specified as a parameter, the error may be relatively large.
1.3 - delayMicroseconds()
void delayMicroseconds(uint32_t microsec)
Performs a delay for the duration specified by microsec
.
Time measurement is done using the TickTimer count. When a long delay is specified, the CPU clock is reduced during polling.
Within the setup()
and wakeup()
functions, since the TickTimer is not yet running, delays are implemented using a while-loop. In this case, the discrepancy from the specified value may be significant. The loop counter is calibrated for 32 MHz. If the CPU clock is altered within these functions, errors proportional to the clock change will occur.
If a short duration such as 10 microseconds or less is specified as a parameter, the error may be relatively large.
1.4 - random()
uint32_t random(uint32_t maxval)
uint32_t random(uint32_t minval, uint32_t maxval)
The first function returns a value in the range 0..(maxval-1)
. Note that the value of maxval is not the maximum value itself.
The second function returns a value in the range minval..maxval-1
.
2 - General Purpose Digital IO
The following functions are used to operate General Purpose Digital IO (DIO).
pinMode()
digitalWrite()
digitalRead()
attachIntDio()
detachIntDio()
Constants
Pin Names and Numbers
Definition | Name |
---|---|
const uint8_t PIN_DIGITAL::DIO0 .. 19 | DIO pins 0 to 19 |
const uint8_t PIN_DIGITAL::DO0 .. 1 | DO pins 0,1 |
Pin Modes (DIO0..19)
The following enumerated values are handled by the type E_PIN_MODE
.
Definition | Pull-up | Name |
---|---|---|
PIN_MODE::INPUT | No | Input |
PIN_MODE::OUTPUT | No | Output |
PIN_MODE::INPUT_PULLUP | Yes | Input with Pull-up |
PIN_MODE::OUTPUT_INIT_HIGH | No | Output (initial state HIGH) |
PIN_MODE::OUTPUT_INIT_LOW | No | Output (initial state LOW) |
PIN_MODE::WAKE_FALLING | No | Input, wake pin, falling edge |
PIN_MODE::WAKE_RISING | No | Input, wake pin, rising edge |
PIN_MODE::WAKE_FALLING_PULLUP | Yes | Input, wake pin, falling edge with pull-up |
PIN_MODE::WAKE_RISING_PULLUP | Yes | Input, wake pin, rising edge with pull-up |
PIN_MODE::DISABLE_OUTPUT | Yes | Return to input state |
Pin Modes (DO0,1)
The following enumerated values are handled by the type E_PIN_MODE
.
Definition | Name |
---|---|
PIN_MODE::OUTPUT | Output |
PIN_MODE::OUTPUT_INIT_HIGH | Output (initial state HIGH) |
PIN_MODE::OUTPUT_INIT_LOW | Output (initial state LOW) |
PIN_MODE::DISABLE_OUTPUT | Stop output setting |
Pin States
The following enumerated values are handled by the type E_PIN_STATE
.
Definition | Value | Name |
---|---|---|
PIN_STATE::HIGH | 1 | HIGH level (=Vcc level) |
PIN_STATE::LOW | 0 | LOW level (=GND level) |
Pin Rising and Falling Edges
The following enumerated values are handled by the type E_PIN_INT_MODE
.
Definition | Name |
---|---|
PIN_INT_MODE::FALLING | Falling edge |
PIN_INT_MODE::RISING | Rising edge |
2.1 - pinMode()
void pinMode(uint8_t u8pin, E_PIN_MODE mode)
This function allows configuration of the states of DIO0 to DIO19 and DO0,1 pins. For details on the configuration values, refer to the enumerated values of E_PIN_MODE
in the DIO explanation and the DO explanation.
DO0 and DO1 are special-purpose pins and are generally used for other functions, but they can also be configured for output. However, these pins have hardware constraints, so caution is required when using them.
Both pins must be held at a HIGH level upon power-up. If the circuit configuration causes unstable voltage levels, it may result in the module failing to start.
2.2 - digitalWrite()
static inline void digitalWrite(uint8_t u8pin, E_PIN_STATE ulVal)
Beforehand, set the target pin as output using pinMode()
. The first parameter specifies the pin number to be set. The second parameter specifies either HIGH
or LOW
.
E_PIN_STATE
. A conversion operator from E_PIN_STATE
to int
is not defined, so direct input by numeric value is not allowed.2.3 - digitalRead()
static inline E_PIN_STATE digitalRead(uint8_t u8pin)
You can get the input value of a pin previously configured as input as LOW
or HIGH
.
E_PIN_STATE
type to int
type is not defined, direct assignment to a numeric type is not possible.2.4 - attachIntDio()
void attachIntDio(uint8_t u8pin, E_PIN_INT_MODE mode)
For a pin configured as input beforehand, the first parameter is the pin number for which you want to enable the interrupt, and the second parameter specifies the interrupt direction (rising edge, falling edge).
Example
Sets up an interrupt that triggers when the DIO5 pin changes from HIGH to LOW.
void setup() {
the_twelite.app.use<myAppClass>();
pinMode(PIN_DIGITAL::DIO5, PIN_MODE::INPUT_PULLUP);
attachIntDio(PIN_DIGITAL::DIO5, PIN_INT_MODE::FALLING);
}
void loop() {
;
}
myAppClass.hpp
class myAppClass: public mwx::BrdPal, MWX_APPDEFS_CRTP(myAppClasslMot)
{
};
Basic definition of the behavior myAppClass
. Details are omitted.
myAppClass.cpp
/*****************************************************************/
// MUST DEFINE CLASS NAME HERE
##define __MWX_APP_CLASS_NAME myAppClass
##include "_mwx_cbs_cpphead.hpp"
/*****************************************************************/
MWX_DIO_INT(PIN_DIGITAL::DIO5, uint32_t arg, uint8_t& handled) {
static uint8_t ct;
digitalWrite(PIN_DIGITAL::DIO12, (++ct & 1) ? HIGH : LOW);
handled = false; // if true, no further event.
}
MWX_DIO_EVENT(PIN_DIGITAL::DIO5, uint32_t arg) {
Serial << '*';
}
/*****************************************************************/
// common procedure (DO NOT REMOVE)
##include "_mwx_cbs_cpptail.cpp"
// MUST UNDEF CLASS NAME HERE
##undef __MWX_APP_CLASS_NAME
} // mwx
/*****************************************************************/
Interrupt handler description for the behavior myAppClass
. When an interrupt occurs on DIO5, it toggles the output setting of DIO12, and after the interrupt handler finishes, an event occurs that prints *
to the serial port Serial
.
2.5 - detachIntDio()
void detachIntDio(uint8_t u8pin)
2.6 - digitalReadBitmap()
uint32_t digitalReadBitmap()
Values are stored in order from the LSB side: DIO0 … DIO19.
Pins on the HIGH
side are set to 1, and pins on the LOW
side are set to 0.
3 - Utility Functions
3.1 - Printf Implementation
printf()
function in C.
int mwx_printf(const char* format, ...)
int mwx_snprintf(char* buffer, size_t count, const char* format, ...)
mwx_printf()
outputs formatted text to the Serial
object. It performs the same processing as Serial.printfmt()
.
mwx_snprintf()
performs snprintf
formatting to a buffer.
mwx::stream
.3.2 - pack_bits()
constexpr uint32_t pack_bits(...)
Parameters are specified as variadic arguments, each indicating a bit position (integer from 0 to 31). For example, pack_bits(1,3,6)
returns ((1UL<<1)|(1UL<<3)|(1UL<<6))
.
constexpr
enables compile-time evaluation when possible using constant expressions.Background
This function simplifies the notation in situations where values are referenced or set in various bitmaps such as IO port (DI, DO) states.
3.3 - collect_bits()
constexpr uint32_t collect_bits(uint32_t bm, ...)
From the value specified in the parameter bm
, this function extracts the values corresponding to the 0..31 bit positions specified by the subsequent variadic parameters. The extracted values are arranged in the order of the parameters and returned as a bitmap.
The bit ordering of the resulting bitmap places the first parameter in the highest bit and the last parameter at bit 0.
uint32_t b1 = 0x12; // (b00010010)
uint32_t b2 = collect_bits(b1, 4, 2, 1, 0);
// bit4->1, bit2->0, bit1->1, bit0->0
// b2=0x10 (b1010)
In this example, bits 4, 2, 1, and 0 of b1
are extracted, resulting in (1,0,1,0). This is interpreted as b1010, resulting in a calculated value of 0x10.
Background
This function simplifies code where values are referenced or set in various bitmaps, such as IO port (DI, DO) statuses.
3.4 - Byte array utils
Reading
Retrieve uint16_t
or uint32_t
values from a byte array interpreted as uint8_t
in big-endian order.
inline uint8_t G_BYTE(const uint8_t*& p) {
return *(p)++;
}
inline uint16_t G_WORD(const uint8_t*& p) {
uint32_t r = *p++;
r = (r << 8) + *p++;
return r;
}
inline uint32_t G_DWORD(const uint8_t*& p) {
uint32_t r = *p++;
r = (r << 8) + *p++;
r = (r << 8) + *p++;
r = (r << 8) + *p++;
return r;
}
p
is incremented by the number of bytes read.
Writing
Writes uint8_t
, uint16_t
, or uint32_t
values in big-endian order to the byte array pointed to by q
.
inline uint8_t& S_OCTET(uint8_t*& q, uint8_t c) {
*q++ = c;
return *q;
}
inline uint8_t& S_WORD(uint8_t*& q, uint16_t c) {
*(q) = ((c) >> 8) & 0xff; (q)++;
*(q) = ((c) & 0xff); (q)++;
return *q;
}
inline uint8_t& S_DWORD(uint8_t*& q, uint32_t c) {
*(q) = ((c) >> 24) & 0xff; (q)++;
*(q) = ((c) >> 16) & 0xff; (q)++;
*(q) = ((c) >> 8) & 0xff; (q)++;
*(q) = ((c) & 0xff); (q)++;
return *q;
}
q
is incremented by the number of bytes written.
Background
These utilities simplify operations during the construction and decomposition of data payloads in wireless packets.
You may also use the simplified pack_bytes()
and expand_bytes()
functions.
3.5 - pack_bytes()
uint8_t* pack_bytes(uint8_t* b, uint8_t* e, ...)
pack_bytes
takes container class begin()
, end()
iterators as parameters and writes the data specified by the following parameters into the container as a byte sequence.
The data types given as variadic arguments are as follows.
Data Type | Bytes | Description |
---|---|---|
uint8_t | 1 | |
uint16_t | 2 | Stored in big-endian order |
uint32_t | 4 | Stored in big-endian order |
uint8_t[N] | N | Fixed-length array of uint8_t |
std::pair<char*,N> | N | Pair of array and length for char* or uint8_t* arrays. Can be created with make_pair() . |
smplbuf_u8& pack_bytes(smplbuf_u8& c, ...)
pack_bytes
takes a container object as a parameter and writes the data specified by the following parameters into the container as a byte sequence. It appends to the end using the container’s .push_back()
method.
The data types given as variadic arguments are as follows.
Data Type | Bytes | Description |
---|---|---|
uint8_t | 1 | |
uint16_t | 2 | Stored in big-endian order |
uint32_t | 4 | Stored in big-endian order |
uint8_t[N] | N | Fixed-length array of uint8_t |
std::pair<char*,N> | N | Pair of array and length for char* or uint8_t* arrays. Can be created with make_pair() . |
smplbuf_u8? | .size() | smplbuf<> container of uint8_t type. Stores data of container length (.size() ). |
Example
auto&& rx = the_twelite.receiver.read();
smplbuf<uint8_t, 128> buf;
mwx::pack_bytes(buf
, uint8_t(rx.get_addr_src_lid()) // src addr (LID)
, uint8_t(0xCC) // cmd id (0xCC, fixed)
, uint8_t(rx.get_psRxDataApp()->u8Seq) // sequence number
, uint32_t(rx.get_addr_src_long()) // src addr (long)
, uint32_t(rx.get_addr_dst()) // dst addr
, uint8_t(rx.get_lqi()) // LQI
, uint16_t(rx.get_length()) // payload length
, rx.get_payload() // payload
);
In this example, attributes and payload of the received packet are re-stored into another buffer buf
.
Background
To simplify the description of byte arrays of type uint8_t
used for generating data payloads of wireless packets and extracting data.
auto&& rx = the_twelite.receiver.read();
uint8_t data[128];
data[0] = rx.get_addr_src_lid();
data[1] = 0xCC;
data[2] = rx.get_psRxDataApp()->u8Seq;
data[4] = rx.get_addr_src_long() & 0x000000FF;
data[5] = (rx.get_addr_src_long() & 0x0000FF00) >> 8;
data[6] = (rx.get_addr_src_long() & 0x00FF0000) >> 16;
data[7] = (rx.get_addr_src_long() & 0xFF000000) >> 24;
...
The above is the simplest description, but a byte array can be generated using Byte array utils as follows.
auto&& rx = the_twelite.receiver.read();
uint8_t data[128], *q = data;
S_OCTET(q, rx.get_addr_src_lid());
S_OCTET(q, 0xCC);
S_OCTET(q, rx.get_psRxDataApp()->u8Seq);
S_DWORD(q, rx.get_addr_src_long());
S_DWORD(q, rx.get_addr_dst());
S_OCTET(q, rx.get_lqi());
S_WORD(q, rx.get_length());
for (auto x : rx.get_payload()) {
S_OCTET(q, x);
}
3.6 - expand-bytes()
const uint8_t* expand_bytes(
const uint8_t* b, const uint8_t* e, ...)
expand_bytes()
takes a combination of iterators of type uint8_t*
as parameters. These specify the beginning of the target to be parsed and the iterator just past the end. If parsing reaches the position of e
, it results in an error and returns nullptr
.
If there is no error in expansion, it returns the iterator for the next reading.
The variable parameters should be specified as follows:
Number of bytes | Data length | Explanation |
---|---|---|
uint8_t | 1 | |
uint16_t | 2 | Expanded as big-endian order |
uint32_t | 4 | Expanded as big-endian order |
uint8_t[N] | N | Fixed-length array of uint8_t |
std::pair<char*,N> | N | A pair of array and array length N of type char* or uint8_t* created by make_pair() |
Example
auto&& rx = the_twelite.receiver.read();
char fourchars[5]{};
auto&& np =
expand_bytes(rx.get_payload().begin(), rx.get_payload().end()
, make_pair((uint8_t*)fourchars, 4)
);
// read rest of payload
uint8_t u8DI_BM_remote = 0xff;
uint16_t au16AI_remote[5];
expand_bytes(np, rx.get_payload().end()
, u8DI_BM_remote
, au16AI_remote[0]
, au16AI_remote[1]
, au16AI_remote[2]
, au16AI_remote[3]
, au16AI_remote[4]
);
In this example, first a 4-byte string is read. Here, make_pair()
is used to explicitly read 4 bytes of data.
Using the returned iterator np
as a base, the next data is read. The next data consists of a uint8_t
type followed by five uint16_t
types.
Background
To simplify the description of byte arrays of type uint8_t
used for generating and extracting data payloads of wireless packets.
auto&& rx = the_twelite.receiver.read();
char fourchars[5]{};
uint8_t u8DI_BM_remote = 0xff;
uint16_t au16AI_remote[5];
uint8_t *p = rx.get_payload().begin();
fourchars[0] = p[0];
fourchars[1] = p[1];
fourchars[2] = p[2];
fourchars[3] = p[3];
fourchars[4] = 0;
p += 4;
u8DI_BM_remote = (p[0] << 8) + p[1]; p+=2;
au16AI_remote[0] = (p[0] << 8) + p[1]; p+=2;
...
The above is the simplest description, but you can read from byte arrays using Byte array utils as follows:
auto&& rx = the_twelite.receiver.read();
char fourchars[5]{};
uint8_t u8DI_BM_remote = 0xff;
uint16_t au16AI_remote[5];
uint8_t *p = rx.get_payload().begin();
fourchars[0] = G_BYTE(p);
fourchars[1] = G_BYTE(p);
fourchars[2] = G_BYTE(p);
fourchars[3] = G_BYTE(p);
fourchars[4] = 0;
u8DI_BM_remote = G_WORD(p);
au16AI_remote[0] = G_WORD(p);
...
3.7 - CRC8, XOR, LRC
uint8_t CRC8_u8Calc(uint8_t *pu8Data, uint8_t size, uint8_t init=0)
uint8_t CRC8_u8CalcU32(uint32_t u32c, uint8_t init=0)
uint8_t CRC8_u8CalcU16(uint16_t u16c, uint8_t init=0)
uint8_t XOR_u8Calc(uint8_t *pu8Data, uint8_t size)
uint8_t LRC_u8Calc(uint8_t* pu8Data, uint8_t size)
Performs calculations for CRC8, XOR, and LRC (used in ASCII format).
CRC8_u8CalcU16()
and CRC8_u8CalcU32()
compute the CRC8 for u16c
and u32c
assuming big-endian order.
X^8+X^5+X^4+1
(polynomial value 0x31), which is sometimes referred to as CRC8-CCITT or CRC8-Maxim.Background
These functions were added as library procedures because they are used for validating wireless packet data sequences, ASCII format checksums (LRC), and various sensor data checks.
3.8 - div10(), div100(), div1000()
struct div_result_i32 {
int32_t quo; // quotient
int16_t rem; // remainder
uint8_t b_neg; // true if negative
uint8_t digits_rem; // digits of remainder
};
div_result_i32 div10(int32_t val);
div_result_i32 div100(int32_t val);
div_result_i32 div1000(int32_t val);
In some cases, sensor values multiplied by 100 are passed as uint16_t
type, but on microcontrollers without division circuits, calculation processing takes considerable time. Therefore, calculations are performed using approximate calculations and corrections with addition, subtraction, multiplication, and bit shifts.
Pass the value to be calculated in val
, the variable to store the remainder in rem
, and the variable to store the sign in neg
.
The return value is the quotient (always positive), rem
contains the remainder (always positive), and neg
stores true
if negative.
Due to algorithm constraints (digit overflow), the calculable value range is limited for div100()
and div1000()
. div100()
supports values from -99999 to 99999, and div1000()
supports values from -999999 to 999999.
Approximate formula to obtain the quotient
div100()
int dv = val * 1311 >> 17;
div1000()
int dv = val * 131 >> 17;
Example usage
auto d1 = div100(sns_val.u16temp_object);
auto d2 = div100(sns_val.u16temp_object);
Serial
<< crlf << format("..Object = %c%2d.%02d"
, d1.b_neg ? '-' : '+', d1.quo, d1.rem)
<< format(" Ambient = %c%2d.%02d"
, d2.b_neg ? '-' : '+', d2.quo, d2.rem);
Calculation speed
About one-tenth of the time.
Output of results
// Conversion options
struct DIVFMT {
static const int STD = 0; // displays with minimal digits (no padding, no positive sign)
static const int PAD_ZERO = 1; // set padding character as '0' instead of ' '.
static const int SIGN_PLUS = 2; // put '+' sign if value is positive or 0.
static const int PAD_ZERO_SIGN_PLUS = 3; // PAD_ZERO & SIGN_PLUS
static const int SIGN_SPACE = 4; // put ' ' sign if value is positive or 0.
static const int PAD_ZERO_SIGN_SPACE = 5; // PAD_ZERO & SIGN_SPACE
};
// Class to store string conversion results
class _div_chars {
...
const char* begin() const {...}
const char* end() const {...}
const char* c_str() const { return begin(); }
operator const char*() const { return begin(); }
};
// format() method
_div_chars div_result_i32::format(
int dig_quo = 0, uint32_t opt = DIVFMT::STD) const;
// Implementation of interface to Serial
template <class D> class stream {
...
inline D& operator << (const mwx::_div_chars&& divc);
inline D& operator << (const mwx::div_result_i32&&);
inline D& operator << (const mwx::div_result_i32&);
};
The div_result_i32
class that stores the division result has a format()
method to obtain a _div_chars
class object. The _div_chars
class object contains a string buffer and provides methods to access the string buffer as const char*
. Also, the <<
operator for the Serial
object is implemented.
The first parameter dig_quo
of the format()
method specifies the number of output digits (excluding the sign). If the output digits are insufficient (below), it is filled with spaces or 0
. The second parameter opt
specifies the format.
opt parameter | Description |
---|---|
DIVFMT::STD | Standard output, fills insufficient digits with spaces, and adds - only for negative values. |
DIVFMT::PAD_ZERO | Fills insufficient digits with 0 . |
DIVFMT::SIGN_PLUS | Adds + sign for positive values as well. |
DIVFMT::PAD_ZERO_SIGN_PLUS | Fills insufficient digits with 0 and adds + sign for positive values. |
DIVFMT::SIGN_SPACE | Adds a space sign instead of + for positive values. |
DIVFMT::PAD_ZERO_SIGN_SPACE | Fills insufficient digits with 0 and adds a space sign instead of + for positive values. |
Example
//// Direct output from div_result_i32 object
Serial << div100(-1234) << crlf;
// Result: -12.34
//// Output with 3 digits
Serial << div100(3456).format(3, DIVFMT::PAD_ZERO_SIGN_PLUE) << crlf;
// Result: +034.56
//// Use c_str() to get const char*
char str1[128];
auto x = div100(-5678);
mwx_snprintf(str1, 16, "VAL=%s", x.format.c_str()); // const char*
Serial << str1;
// Result: VAL=-56.78
Background
In TWELITE BLUE/RED, division is a costly operation, so a division algorithm with limited purposes was added.
Within the library, some sensor values such as temperature and humidity are represented using values multiplied by 100 (e.g., 25.12℃ as 2512), so a simple procedure to obtain the quotient and remainder when divided by 100 was defined.
dev_result_i32::format()
is provided to avoid complexity when formatting output.
3.9 - Scale utils
x*1000/255
) is replaced with multiplication and bit shifts for approximate calculation.
static inline uint8_t scale_1000_to_127u8(uint16_t x)
static inline uint16_t scale_127u8_to_1000(uint8_t x)
static inline uint8_t scale_1000_to_255u8(uint16_t x)
static inline uint16_t scale_255u8_to_1000(uint8_t x)
static inline uint8_t scale_1000_to_256u8(uint16_t x)
static inline uint16_t scale_256u16_to_1000(uint16_t x)
scale_1000_to_127u8()
Scales 0..1000 to 0..127. Uses (16646*x+65000)>>17
for approximate calculation.
scale_127u8_to_1000()
Scales 0..127 to 0..1000. Uses (2064000UL*x+131072)>>18
for approximate calculation.
scale_1000_to_255u8()
Scales 0..1000 to 0..255. Uses (33423*x+65000)>>17
for approximate calculation.
scale_255u8_to_1000()
Scales 0..255 to 0..1000. Uses (1028000UL*uint32_t(x)+131072)>>18
for approximate calculation.
scale_1000_to_256u8()
Scales 0..1000 to 0..256. Uses (33554*x+66000) >> 17
for approximate calculation.
Note: For x=999,1000 the calculated value becomes 256, but returns 255 as the range of uint8_t
.
scale_256u16_to_1000()
Scales 0..256 to 0..1000. Uses (1028000UL*uint32_t(x)+131072)>>18
for approximate calculation.
Background
Values to be set in hardware are often based on binary such as 0..255, while numbers handled in user applications are easier to manage when based on decimal such as 0..1000. These scale conversions define formulas that do not use division.
3.10 - pnew()
template <class T, class... Args>
T* pnew(T& obj, Args&&... args) {
return (T*)new ((void*)&obj) T(std::forward<Args&&>(args)...);
}
For example, it can be used as follows. You can also pass constructor arguments.
class my_class {
int _a;
public:
my_class(int a = -1) : _a(a) {}
};
my_class obj_1; // This constructor is not called!
my_class obj_2; // This constructor is not called!
void setup() {
mwx::pnew(obj_1); // Equivalent to my_class obj_1;
mwx::pnew(obj_2, 2); // Equivalent to my_class obj_2(2);
...
}
Background
Due to compiler constraints, constructors of global objects are not called. One method to initialize them is using placement new. However, the placement new syntax can appear verbose.
Another method is to use std::unique_ptr
(or eastl::unique_ptr
).
std::unique_ptr<my_class> obj_3;
void setup() {
obj_3.reset(new my_class(3));
// On TWELITE microcontrollers, `new` can only allocate once and `delete` cannot be used,
// so in practice it is equivalent to a global object.
}