Tuesday, August 18, 2020

Digest for comp.lang.c++@googlegroups.com - 10 updates in 3 topics

Frederick Gotham <cauldwell.thomas@gmail.com>: Aug 18 06:32AM -0700

At the moment I'm taking the encryption technique I've coded in multithreaded C++ code, and porting it to single-threaded C for use on a microcontroller.
 
To demonstrate how I'm handling so-called 'polymorphic' function calls, let's start off with this C++ program:
 
#include <cstddef> /* size_t */
#include <cstring> /* strlen, strcpy */
 
#include <iostream> /* cout, endl */
 
class Parser {
protected:
 
char *_name;
bool _should_tell_name;
 
public:
 
Parser(char const *const arg, bool const shareable) : _should_tell_name(shareable)
{
_name = new char[std::strlen(arg)];
std::strcpy(_name,arg);

std::cout << "Object constructed" << std::endl;
}
 
virtual ~Parser(void)
{
delete [] _name;

std::cout << "Object destructed" << std::endl;
}
 
virtual bool CanTellName(void) const { return _should_tell_name; }
 
virtual char const *GetName(void) const
{
return CanTellName() ? _name : "No Comment";
}
};
 
int main()
{
Parser obj("Frederick", false);

std::cout << obj.GetName() << std::endl;
}
 
 
Well the first thing I do to turn this into C is to change the included header filenames, and replace cout's and new's:
 
#include <stddef.h> /* size_t */
#include <string.h> /* strlen, strcpy */
#include <stdlib.h> /* malloc, free */
#include <stdio.h> /* puts */
 
typedef int intbool; /* Because C90 doesn't have bool */
 
class Parser {
protected:
 
char *_name;
intbool _should_tell_name;
 
public:
 
Parser(char const *const arg, intbool const shareable) : _should_tell_name(shareable)
{
_name = (char*)malloc(strlen(arg));
strcpy(_name,arg);

puts("Object constructed");
}
 
virtual ~Parser(void)
{
free(_name);

puts("Object destructed");
}
 
virtual intbool CanTellName(void) const { return _should_tell_name; }
 
virtual char const *GetName(void) const
{
return CanTellName() ? _name : "No Comment";
}
};
 
int main()
{
Parser obj("Frederick", true);

puts(obj.GetName());
}
 
 
 
Next I turn the class into a POD struct with a Vtable as follows, the following with compile with a C compiler:
 
 
#include <stddef.h> /* size_t */
#include <string.h> /* strlen, strcpy */
#include <stdlib.h> /* malloc, free */
#include <stdio.h> /* puts */
 
typedef int intbool; /* Because C90 doesn't have bool */
 
struct Parser_Vtable {
void (*Destructor)(void);
intbool (*CanTellName)(void) /* const */ ;
char const *(*GetName)(void) /* const */ ;
};
 
void Parser_Detail_Destructor (void);
intbool Parser_Detail_CanTellName(void) /* const */ ;
char const *Parser_Detail_GetName (void) /* const */ ;
 
struct Parser_Vtable const g_parser_vtable = {
&Parser_Detail_Destructor,
&Parser_Detail_CanTellName,
&Parser_Detail_GetName
};
 
struct Parser {
struct Parser_Vtable const *vtable;
 
char *_name;
intbool _should_tell_name;
};
 
void Parser_Constructor(struct Parser *const this, char const *const arg, intbool const shareable)
{
this->_should_tell_name = shareable;
this->_name = (char*)malloc(strlen(arg));
strcpy(this->_name,arg);
 
puts("Object constructed");
}
 
int main()
{
struct Parser obj;

Parser_Constructor(&obj, "Frederick", 1 /*true*/);

/* puts(obj.GetName()); - - What do we do here ? */
}
 
 
Next I'll implement the three virtual functions in C:
 
 
void Parser_Detail_Destructor(void)
{
struct Parser *const this = Pop_This();

free(this->_name);
 
puts("Object destructed");
}
 
intbool Parser_Detail_CanTellName(void) /* const */
{
struct Parser const *const this = Pop_This_Const();
 
return this->_should_tell_name;
}
 
char const *Parser_Detail_GetName(void) /* const */
{
struct Parser const *const this = Pop_This_Const();
 
/* I will explain this next line in just a minute */
return Virtcall(this,CanTellName)() ? this->_name : "No Comment";
}
 
 
In order to get this all to work, here's my helper functions, and my macro "Virtcall" which is used for invoking a virtual method on an object:
 
void *g_stack_of_this[16] = {0};
 
static void Push_This(void const *const arg)
{
void **p = g_stack_of_this + 0u;

while ( 0 != *p ) ++p;

*p = (void*)arg; /* Okay to discard const here if correct form of Pop_This is used */
}
 
static void *Pop_This(void)
{
void *retval;

void **q = g_stack_of_this + (sizeof g_stack_of_this / sizeof *g_stack_of_this) - 1u;

while ( 0 == *q ) --q;

retval = *q;

*q = 0;

return retval;
}
 
static void const *Pop_This_Const(void) { return (void const*)Pop_This(); }
 
#define Virtcall(p, method) ( Push_This((p)) , ((p))->vtable->method )
 
 
The macro "Virtcall" yields a function pointer, so you can then put "()" after it to call the function with no arguments.
 
This code I've written isn't threadsafe because there is only one "g_stack_of_this" for the entire process. On a newer C compiler (e.g. C11), you could use the "_Thread_local" keyword on this global array to make everything threadsafe.
 
So anyway this is how I'm going about porting my C++ encryption technique to C.
 
Here's the full code:
 
#include <stddef.h> /* size_t */
#include <string.h> /* strlen, strcpy */
#include <stdlib.h> /* malloc, free */
#include <stdio.h> /* puts */
 
void *g_stack_of_this[16] = {0};
 
static void Push_This(void const *const arg)
{
void **p = g_stack_of_this + 0u;

while ( 0 != *p ) ++p;

*p = (void*)arg; /* Okay to discard const here if correct form of Pop_This is used */
}
 
static void *Pop_This(void)
{
void *retval;

void **q = g_stack_of_this + (sizeof g_stack_of_this / sizeof *g_stack_of_this) - 1u;

while ( 0 == *q ) --q;

retval = *q;

*q = 0;

return retval;
}
 
static void const *Pop_This_Const(void) { return (void const*)Pop_This(); }
 
#define Virtcall(p, method) ( Push_This((p)) , ((p))->vtable->method )
 
typedef int intbool; /* Because C90 doesn't have bool */
 
struct Parser_Vtable {
void (*Destructor)(void);
intbool (*CanTellName)(void) /* const */ ;
char const *(*GetName)(void) /* const */ ;
};
 
void Parser_Detail_Destructor (void);
intbool Parser_Detail_CanTellName(void) /* const */ ;
char const *Parser_Detail_GetName (void) /* const */ ;
 
struct Parser_Vtable const g_parser_vtable = {
&Parser_Detail_Destructor,
&Parser_Detail_CanTellName,
&Parser_Detail_GetName
};
 
struct Parser {
struct Parser_Vtable const *vtable;
 
char *_name;
intbool _should_tell_name;
};
 
void Parser_Constructor(struct Parser *const this, char const *const arg, intbool const shareable)
{
this->vtable = &g_parser_vtable;
 
this->_should_tell_name = shareable;
this->_name = (char*)malloc(strlen(arg));
strcpy(this->_name,arg);
 
puts("Object constructed");
}
 
int main()
{
struct Parser obj;

Parser_Constructor(&obj, "Frederick", 1 /*true*/);

puts( Virtcall(&obj,GetName)() );

Virtcall(&obj,Destructor)();
 
return 0;
}
 
void Parser_Detail_Destructor(void)
{
struct Parser *const this = Pop_This();

free(this->_name);
 
puts("Object destructed");
}
 
intbool Parser_Detail_CanTellName(void) /* const */
{
struct Parser const *const this = Pop_This_Const();
 
return this->_should_tell_name;
}
 
char const *Parser_Detail_GetName(void) /* const */
{
struct Parser const *const this = Pop_This_Const();
 
return Virtcall(this,CanTellName)() ? this->_name : "No Comment";
}
 
Frederick
Frederick Gotham <cauldwell.thomas@gmail.com>: Aug 18 11:20AM -0700

> _name = new char[std::strlen(arg)];
> std::strcpy(_name,arg);
 
 
Array is one byte too small.
 
 
> this->_name = (char*)malloc(strlen(arg));
> strcpy(this->_name,arg);
 
 
Same bug here.
David Brown <david.brown@hesbynett.no>: Aug 18 11:15AM +0200

On 17/08/2020 19:35, Juha Nieminen wrote:
> 20 years ago. This year.
 
> It is perfectly possible. You just have to watch for that 32 kB
> limit.
 
All sorts of programs can be written for small microcontrollers - I have
used far smaller ones. And you can use C++ (with limitations and
restrictions) on small microcontrollers.
 
But you can't use C++ on a PIC18. I don't know the state of Microchip's
C compiler these days - based on what I saw in the past, I'm suspicious
of its quality. (There are good quality PIC18 compilers from other
vendors, at serious prices.)
 
The killer limit for the PIC18 here is the 3K ram space, not the 128K
flash limit, and this is a core that does not do well with pointers -
they are very inefficient. With just 3K of ram, use of dynamic memory
is pretty much out of the question, which is a very painful restriction
for any kind of networking. The MAC on the chip has 8K dedicated ram
for packets, without which Ethernet would be nearly impossible, but with
3K general ram, basic network standards like DNS, DHCP and TCP/IP are
almost out of the question - they are possible, but only with crippling
restrictions.
Juha Nieminen <nospam@thanks.invalid>: Aug 18 10:03AM

> 3K general ram, basic network standards like DNS, DHCP and TCP/IP are
> almost out of the question - they are possible, but only with crippling
> restrictions.
 
Aren't dedicated chips (of the likes of KSZ9897) used to handle ethernet
in such restricted embedded systems?
David Brown <david.brown@hesbynett.no>: Aug 18 01:22PM +0200

On 18/08/2020 12:03, Juha Nieminen wrote:
>> restrictions.
 
> Aren't dedicated chips (of the likes of KSZ9897) used to handle ethernet
> in such restricted embedded systems?
 
That's a switch chip - you use it for making an Ethernet switch, not for
communicating over Ethernet. (If you open up a simple off-the-shelf
4-port Ethernet switch, it will have a chip like that in the middle, and
no microcontroller.)
 
There are chips, or modules, that are made for adding Ethernet to
existing small microcontroller designs. There the chip (or module) has
the software to implement TCP/IP, and usually at least some of HTTP,
HTTPS, DNS, SMTP, etc., as servers and/or clients. The chip
communicates with the microcontroller by UART or SPI. The idea is that
as much as possible is handled on that chip, and the microcontroller is
only responsible for executing commands or returning data. You use such
devices when you have an existing design that you want to upgrade to use
Ethernet (or Wifi) but don't want to re-do the design using a more
powerful and modern device.
 
For a while, many years ago (maybe 15-20 years), there was a demand for
small microcontrollers with Ethernet support. At that time, 32-bit
devices and reasonable ram sizes were big, expensive, and hard to use -
so 8-bit and 16-bit devices were much more popular. There was a market
for such things with very limited and simple network communication -
things like embedded web servers where the page would fit within one
Ethernet frame so that you didn't need fragmentation, and where a TCP/IP
retry was handled by the application re-creating the dynamic page with
the old data because you didn't have enough ram do buffer anything. The
networking code was highly specialised and tied tightly to the
application code, because there were no resources for generalisation.
 
The PIC18 discussed here is from that era.
 
These days, you get 32-bit microcontrollers with plenty of flash and ram
and a 100 Mbit MAC for a fraction of the price of the PIC18. There are
off-the-shelf network stacks and plenty of examples around, and you have
enough power to have a web server, and a Modbus server, and telnet
servers, and SSL encryption and whatever else you may want for a useful
device that is suitable for today's networks.
boltar@nowhere.co.uk: Aug 18 05:49PM

On Tue, 18 Aug 2020 11:15:37 +0200
>3K general ram, basic network standards like DNS, DHCP and TCP/IP are
>almost out of the question - they are possible, but only with crippling
>restrictions.
 
Why would you even bother? If you need proper networking use a PIC with a
bigger memory. They cost buttons and there are plenty to choose from.
Sam <sam@email-scan.com>: Aug 17 07:30PM -0400

Juha Nieminen writes:
 
> >> tantrums and insults like a petulant child?
 
> > Yes, I do: they suck.
 
> So a petulant child you are. I will thus be treating you as one.
 
You are operating under an impression that I value your opinions very
highly. Not sure where you got that impression from.
red floyd <no.spam.here@its.invalid>: Aug 17 08:22PM -0700

On 8/16/2020 11:24 PM, David Brown wrote:
> "invent" coroutines. Amongst others, they are discussed by Knuth - who
> describes subroutines (i.e., "functions" in C and C++) as just being
> special cases of coroutines.
 
In addition, Coroutines (called Tasks) were actually part of Modula-2
language specification (as of 1983).
red floyd <no.spam.here@its.invalid>: Aug 17 08:22PM -0700

On 8/17/2020 8:22 PM, red floyd wrote:
>> special cases of coroutines.
 
> In addition, Coroutines (called Tasks) were actually part of Modula-2
> language specification (as of 1983).
 
Pardon me, they were called "Processes"
"Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com>: Aug 18 07:53AM +0200

On 18.08.2020 05:22, red floyd wrote:
 
>> In addition, Coroutines (called Tasks) were actually part of Modula-2
>> language specification (as of 1983).
 
> Pardon me, they were called "Processes"
 
They were called coroutines.
 
Possibly you're thinking of Modula-2's asynchronous coroutines.
 
There was a book about development in Modula-2 I had (still have in a
box somewhere) called "Modula-2: A Software Development Approach" by
Ford & Wiener, which presented an abstraction over coroutines and
asynchronous interrupt-driven coroutines. A kind of threading
abstraction where at the higher levels you code up things without
knowing whether it will execute all in one single thread or in several,
whatever.
 
It's an old idea, e.g. one Algol compiler in the early 1960's was based
on coroutines where it could execute either as one single process, or as
sequentially invoked multiple phases.
 
I mentioned the Modula-2 abstraction idea to Bjarne when he presented
`std::future` and friends in Oslo mid 2000's. However, he appeared just
perplexed at my enthusiasm for the idea. At that time coroutines were
not yet on the table for C++, so maybe discussing an abstraction over
something he presented + something not yet envisioned, was premature...
 
 
- Alf
You received this digest because you're subscribed to updates for this group. You can change your settings on the group membership page.
To unsubscribe from this group and stop receiving emails from it send an email to comp.lang.c+++unsubscribe@googlegroups.com.

No comments: