Date: Tue, 7 Jan 2025 07:33:26 +0000
On Tuesday, January 7, 2025, Tiago Freire wrote:
> Frederick wrote: Are you sure we even need 'goto' anymore? Even at
> runtime?
>
> Yes, I have written a couple myself. Most often in the context of parsing
> where special tokens would cause a skip of a procedure.
> Where typically you have to do A B and C in order.
> A is a loop, but if a special token is hit what you need to do is not only
> to break out of A but to skip B entirely and go straight to C.
> Attempts at re-writing this pattern in terms of functions/alternative code
> flows/special states has always resulted in worst code and much more
> spaghetti.
> A goto in this case prevents the spaghetti instead of causing it, it is
> the best tool for the job as it does exactly what you want and prevents the
> need to track extra state.
>
I dare you to post that code here and I'll try to get rid of the goto's in
a nice way.
I probably use goto a lot more than the average programmer -- especially
with microcontrollers. Just now I searched my GitHub account for goto's. I
wrote this for an Arduino ESP32 microcontroller to refresh the LCD display
to show the status of the WiFi and GSM connections:
void oled_loop(void)
{
static bool already_done = false;
static long long unsigned timestamp = 0u;
if ( (millis64() - timestamp) < 5000u ) return;
timestamp = millis64();
/* Things that can change:
Sim status
Wifi status
g_str_display */
static SimGsm::Status previous_simgsm_status = SimGsm::disabled;
static int previous_wifi_status = !static_cast<int>(WL_CONNECTED);
static char previous_str_display[sizeof g_str_display] = {};
SimGsm::Status const current_simgsm_status = gSimGsm.GetStatus();
int const current_wifi_status = WiFi.status() ;
if ( false == already_done ) goto RefreshDisplay;
if ( previous_simgsm_status != current_simgsm_status ) goto
RefreshDisplay;
if ( previous_wifi_status != current_wifi_status ) goto
RefreshDisplay;
if ( 0 != strcmp(g_str_display, previous_str_display) ) goto
RefreshDisplay;
return;
RefreshDisplay:
already_done = true;
previous_simgsm_status = current_simgsm_status;
previous_wifi_status = current_wifi_status ;
std::strcpy(previous_str_display,g_str_display);
display.clearDisplay();
display.display();
}
And here's some C code I wrote for Linux to start an embedded program as a
super user (yes the antivirus had a field day):
int main(void)
{
/* create the shared memory object */
int f = shm_open("/prog_binary", O_CREAT|O_RDWR,
S_IXUSR|S_IXGRP|S_IXOTH);
if ( f < 0 )
return EXIT_FAILURE;
int retval = EXIT_FAILURE;
/* configure the size of the shared memory object */
if ( 0 != ftruncate(f, g_progSize) )
goto Clean_Up;
/* memory map the shared memory object */
void *const ptr = mmap(0, g_progSize, PROT_READ|PROT_WRITE|PROT_EXEC,
MAP_SHARED, f, 0);
if ( NULL == ptr )
goto Clean_Up;
/* write to the shared memory object */
memcpy(ptr, g_progData, g_progSize);
munmap(ptr, g_progSize);
close(f);
f = -1; /* DON'T REMOVE THIS LINE */
int const main_prog_retval = system("pkexec env DISPLAY=$DISPLAY
XAUTHORITY=$XAUTHORITY /dev/shm/prog_binary");
if ( -1 != main_prog_retval )
retval = WEXITSTATUS(main_prog_retval);
Clean_Up:
if ( f >= 0 )
close(f);
shm_unlink("/prog_binary");
return retval;
}
And here's what ChatGPT gave me back when I asked it to remove all uses of
'goto':
void oled_loop(void)
{
static bool already_done = false;
static long long unsigned timestamp = 0u;
if ((millis64() - timestamp) < 5000u) return;
timestamp = millis64();
/* Things that can change:
Sim status
Wifi status
g_str_display */
static SimGsm::Status previous_simgsm_status = SimGsm::disabled;
static int previous_wifi_status = !static_cast<int>(WL_CONNECTED);
static char previous_str_display[sizeof g_str_display] = {};
SimGsm::Status const current_simgsm_status = gSimGsm.GetStatus();
int const current_wifi_status = WiFi.status();
// Check if display needs to be refreshed
bool refresh_display = false;
if (!already_done)
refresh_display = true;
else if (previous_simgsm_status != current_simgsm_status)
refresh_display = true;
else if (previous_wifi_status != current_wifi_status)
refresh_display = true;
else if (0 != strcmp(g_str_display, previous_str_display))
refresh_display = true;
if ( !refresh_display ) return;
already_done = true;
previous_simgsm_status = current_simgsm_status;
previous_wifi_status = current_wifi_status;
std::strcpy(previous_str_display, g_str_display);
display.clearDisplay();
display.display();
}
int main(void)
{
/* create the shared memory object */
int f = shm_open("/prog_binary", O_CREAT | O_RDWR, S_IXUSR | S_IXGRP |
S_IXOTH);
if (f < 0)
return EXIT_FAILURE;
int retval = EXIT_FAILURE;
void *ptr = nullptr; // Declare pointer for cleanup management
do {
/* configure the size of the shared memory object */
if (ftruncate(f, g_progSize) != 0)
break;
/* memory map the shared memory object */
ptr = mmap(0, g_progSize, PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_SHARED, f, 0);
if (ptr == NULL)
break;
/* write to the shared memory object */
memcpy(ptr, g_progData, g_progSize);
munmap(ptr, g_progSize);
ptr = nullptr; // Ensure ptr is not cleaned up again
close(f);
f = -1; // Reset file descriptor to avoid cleanup
int const main_prog_retval = system("pkexec env DISPLAY=$DISPLAY
XAUTHORITY=$XAUTHORITY /dev/shm/prog_binary");
if (main_prog_retval != -1)
retval = WEXITSTATUS(main_prog_retval);
} while (false);
// Cleanup block
if (ptr != nullptr)
munmap(ptr, g_progSize);
if (f >= 0)
close(f);
shm_unlink("/prog_binary");
return retval;
}
If someone out there is shouting saying they need constexpr goto, then post
some code here and let's see just how badly it's needed. Your solution will
probably be as simple as "Dear ChatGPT, I have to edit the following
function to make it constexpr, which means I need to get rid of the goto's,
so please try to get rid of the goto's making the code as simple and easy
to read as possible."
I reckon Tiago's token parsing code can even be done nicely.
> Frederick wrote: Are you sure we even need 'goto' anymore? Even at
> runtime?
>
> Yes, I have written a couple myself. Most often in the context of parsing
> where special tokens would cause a skip of a procedure.
> Where typically you have to do A B and C in order.
> A is a loop, but if a special token is hit what you need to do is not only
> to break out of A but to skip B entirely and go straight to C.
> Attempts at re-writing this pattern in terms of functions/alternative code
> flows/special states has always resulted in worst code and much more
> spaghetti.
> A goto in this case prevents the spaghetti instead of causing it, it is
> the best tool for the job as it does exactly what you want and prevents the
> need to track extra state.
>
I dare you to post that code here and I'll try to get rid of the goto's in
a nice way.
I probably use goto a lot more than the average programmer -- especially
with microcontrollers. Just now I searched my GitHub account for goto's. I
wrote this for an Arduino ESP32 microcontroller to refresh the LCD display
to show the status of the WiFi and GSM connections:
void oled_loop(void)
{
static bool already_done = false;
static long long unsigned timestamp = 0u;
if ( (millis64() - timestamp) < 5000u ) return;
timestamp = millis64();
/* Things that can change:
Sim status
Wifi status
g_str_display */
static SimGsm::Status previous_simgsm_status = SimGsm::disabled;
static int previous_wifi_status = !static_cast<int>(WL_CONNECTED);
static char previous_str_display[sizeof g_str_display] = {};
SimGsm::Status const current_simgsm_status = gSimGsm.GetStatus();
int const current_wifi_status = WiFi.status() ;
if ( false == already_done ) goto RefreshDisplay;
if ( previous_simgsm_status != current_simgsm_status ) goto
RefreshDisplay;
if ( previous_wifi_status != current_wifi_status ) goto
RefreshDisplay;
if ( 0 != strcmp(g_str_display, previous_str_display) ) goto
RefreshDisplay;
return;
RefreshDisplay:
already_done = true;
previous_simgsm_status = current_simgsm_status;
previous_wifi_status = current_wifi_status ;
std::strcpy(previous_str_display,g_str_display);
display.clearDisplay();
display.display();
}
And here's some C code I wrote for Linux to start an embedded program as a
super user (yes the antivirus had a field day):
int main(void)
{
/* create the shared memory object */
int f = shm_open("/prog_binary", O_CREAT|O_RDWR,
S_IXUSR|S_IXGRP|S_IXOTH);
if ( f < 0 )
return EXIT_FAILURE;
int retval = EXIT_FAILURE;
/* configure the size of the shared memory object */
if ( 0 != ftruncate(f, g_progSize) )
goto Clean_Up;
/* memory map the shared memory object */
void *const ptr = mmap(0, g_progSize, PROT_READ|PROT_WRITE|PROT_EXEC,
MAP_SHARED, f, 0);
if ( NULL == ptr )
goto Clean_Up;
/* write to the shared memory object */
memcpy(ptr, g_progData, g_progSize);
munmap(ptr, g_progSize);
close(f);
f = -1; /* DON'T REMOVE THIS LINE */
int const main_prog_retval = system("pkexec env DISPLAY=$DISPLAY
XAUTHORITY=$XAUTHORITY /dev/shm/prog_binary");
if ( -1 != main_prog_retval )
retval = WEXITSTATUS(main_prog_retval);
Clean_Up:
if ( f >= 0 )
close(f);
shm_unlink("/prog_binary");
return retval;
}
And here's what ChatGPT gave me back when I asked it to remove all uses of
'goto':
void oled_loop(void)
{
static bool already_done = false;
static long long unsigned timestamp = 0u;
if ((millis64() - timestamp) < 5000u) return;
timestamp = millis64();
/* Things that can change:
Sim status
Wifi status
g_str_display */
static SimGsm::Status previous_simgsm_status = SimGsm::disabled;
static int previous_wifi_status = !static_cast<int>(WL_CONNECTED);
static char previous_str_display[sizeof g_str_display] = {};
SimGsm::Status const current_simgsm_status = gSimGsm.GetStatus();
int const current_wifi_status = WiFi.status();
// Check if display needs to be refreshed
bool refresh_display = false;
if (!already_done)
refresh_display = true;
else if (previous_simgsm_status != current_simgsm_status)
refresh_display = true;
else if (previous_wifi_status != current_wifi_status)
refresh_display = true;
else if (0 != strcmp(g_str_display, previous_str_display))
refresh_display = true;
if ( !refresh_display ) return;
already_done = true;
previous_simgsm_status = current_simgsm_status;
previous_wifi_status = current_wifi_status;
std::strcpy(previous_str_display, g_str_display);
display.clearDisplay();
display.display();
}
int main(void)
{
/* create the shared memory object */
int f = shm_open("/prog_binary", O_CREAT | O_RDWR, S_IXUSR | S_IXGRP |
S_IXOTH);
if (f < 0)
return EXIT_FAILURE;
int retval = EXIT_FAILURE;
void *ptr = nullptr; // Declare pointer for cleanup management
do {
/* configure the size of the shared memory object */
if (ftruncate(f, g_progSize) != 0)
break;
/* memory map the shared memory object */
ptr = mmap(0, g_progSize, PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_SHARED, f, 0);
if (ptr == NULL)
break;
/* write to the shared memory object */
memcpy(ptr, g_progData, g_progSize);
munmap(ptr, g_progSize);
ptr = nullptr; // Ensure ptr is not cleaned up again
close(f);
f = -1; // Reset file descriptor to avoid cleanup
int const main_prog_retval = system("pkexec env DISPLAY=$DISPLAY
XAUTHORITY=$XAUTHORITY /dev/shm/prog_binary");
if (main_prog_retval != -1)
retval = WEXITSTATUS(main_prog_retval);
} while (false);
// Cleanup block
if (ptr != nullptr)
munmap(ptr, g_progSize);
if (f >= 0)
close(f);
shm_unlink("/prog_binary");
return retval;
}
If someone out there is shouting saying they need constexpr goto, then post
some code here and let's see just how badly it's needed. Your solution will
probably be as simple as "Dear ChatGPT, I have to edit the following
function to make it constexpr, which means I need to get rid of the goto's,
so please try to get rid of the goto's making the code as simple and easy
to read as possible."
I reckon Tiago's token parsing code can even be done nicely.
Received on 2025-01-07 07:33:29