12. UART Write Overloading

Simulate a simplified UART driver that supports multiple kinds of data transmission.

You must create exactly three overloaded functions named write, each handling a different type of UART transmission:

  • One overload must handle sending a single byte value.
  • One overload must handle sending a text string.
  • One overload must handle sending a buffer of bytes.
     

Program Behavior

In main():

  • Read an integer mode:
    • 1 → send a single byte
    • 2 → send a string
    • 3 → send a buffer of bytes
  • Depending on the mode:
    • Read the appropriate data
    • Call the correct overloaded version of write
    • Print the transmitted output exactly as described

       

Example  (Mode 1)

Input:

1 65

Output:

65

 

Example  (Mode 2)

Input:

2 Hello

Output:

Hello

 

Example  (Mode 3)

Input:

3 4 10 20 30 40

Output:

10 20 30 40

 

Input / Output Rules

  • Mode 1
    • Input: 1 <byte>
      Output: <byte>
  • Mode 2
    • Input: 2 <string>
      Output: <string>
  • Mode 3
    • Input: 3 <length> <byte1> <byte2> ...
      Output: <byte1> <byte2> ...

 

Output formatting requirements:

  • Bytes must be separated by single spaces
  • No trailing space
  • No extra newline
     

Constraints:

  • Implement exactly three overloaded functions named write
  • Each overload must accept different parameter types
  • The overload set must be unambiguous across compilers
  • Single byte values are numeric and in the range 0–255
  • Buffer length n is in the range 0–100
  • Buffer values are numeric bytes in the range 0–255
  • String input contains no spaces (read using cin >> s)
  • Buffer input values must be treated as raw byte values, not ASCII characters

 

 

 


 

Need Help? Refer to the Quick Guide below

In C, every function must have a unique name (e.g., print_int, print_string, print_float).

In C++, Function Overloading allows multiple functions to share the same name, provided they have different parameter lists (number or types of arguments).

The compiler decides which function to call at compile time based on the arguments passed. This is known as Static Resolution.

Syntax & Usage

1. Basic Overloading

You can define multiple versions of a log() function to handle different data types cleanly.

void log(int val) {
    // Code to print integer to UART
}

void log(const char* msg) {
    // Code to print string to UART
}

void log(double val) {
    // Code to print float to UART
}

// Usage
log(100);       // Calls log(int)
log("Error");   // Calls log(const char*)
log(3.14);      // Calls log(double)

2. Overloading by Number of Arguments

Useful for providing default behaviors or extended options.

// Initialize timer with default prescaler
void timer_init(int period_ms);

// Initialize timer with specific prescaler
void timer_init(int period_ms, int prescaler);

How It Works: Name Mangling

C++ supports this by "mangling" the function names. The compiler adds information about the parameters to the internal symbol name.

Function DeclarationInternal Linker Symbol (Example)
void func(int)_Z4funci
void func(double)_Z4funcd
void func(int, int)_Z4funcii

Note: This is why you must use extern "C" when calling C functions from C++ (to stop mangling).

Relevance in Embedded/Firmware

1. Cleaner HAL/Driver APIs

Instead of remembering uart_send_byte, uart_send_array, uart_send_string, you simply use uart.send(). The compiler picks the right driver function for you.

serial.print(10);      // Easy to read
serial.print("Hello"); // Consistent interface

2. Constructor Overloading

A peripheral driver can have multiple constructors to support different initialization modes (e.g., one for default settings, one for custom pins).

// Default I2C (uses standard pins)
I2C_Driver sensor_bus; 

// Remapped I2C (uses custom pins)
I2C_Driver sensor_bus(PB_9, PB_8); 

3. Type-Safe Alternatives to void*

In C, generic handlers often take void* and size, requiring unsafe casting. Overloading lets you write specific, type-safe handlers for int, struct, or float without casting.

Common Pitfalls (Practical Tips)

PitfallDetails
❌ Return Type Only

You cannot overload based only on return type.

int get(); and void get(); will cause a compile error.

❌ Implicit Conversions

Passing 0 (int) to a function expecting a pointer (char*) or double can trigger the wrong overload or ambiguity.

Fix: Use nullptr or explicit casts.

❌ Code Bloat

Remember: Overloaded functions are separate functions in Flash memory. They share a name, not code. If you implement logic 3 times, you use 3x the flash.

Fix: Have all overloads call a single private "worker" function to share logic.

✅ Default ArgumentsSometimes using Default Arguments (void func(int x, int y=0)) is better than overloading if the code logic is identical.
extern "C" ConflictsYou cannot overload functions inside an extern "C" block because C does not support name mangling.