Reading delimited packets with Indy TIdTCPServer in C++Builder - c++builder

I have a microprocessor device that wants to connect to a TCP server and then send a token each time its input state changes. The tokens are in the form "AS9=0;" with no CR or LF. Each token is sent in one packet identified with a length of 6. For my project to work properly, I want to allow the connection to open, receive the data, and use it without waiting for another packed or closing the connection.
The socket component set that comes with C++Builder is Indy 10. It represents an astonishing amount of work. However, all that work is in Pascal, not much use to me. It would be generous to say that the documentation is difficult. Ideally, I would like to read the data on a packet per packet basis. I can identify no means to do this. I have tried the AllData() method. Although it works, it waits until the connection is closed to read out the data. This is not usable for me. I have also tried the ReadLn() method. It reads out the data once it encounters a LF. It too is useless as my data contains no LFs.
So far, my code looks like this:
void __fastcall TForm1::IdTCPServer1Execute(TIdContext *AContext)
{
UnicodeString mystring;
RichEdit1->Lines->Add(" * ");
mystring = AContext->Connection->IOHandler->AllData();
RichEdit1->Lines->Add(mystring + " *** ");
}
//-----------------------------------
void __fastcall TForm1::IdTCPServer1Disconnect(TIdContext *AContext)
{
RichEdit1->Lines->Add("Disconnected. . . \r\n");
}
//--------------------------------------
void __fastcall TForm1::IdTCPServer1Connect(TIdContext *AContext)
{
RichEdit1->Lines->Add("Connected. . . \r\n");
}
//---------------------------------------
As I mentioned, the above code will show everything, but only once the connection is closed. Given all of this, I have two questions. First, how can I grab data as soon as the packet comes in? Second, for the ReadLn(), how can I change the line delimiter from a LF to a ";"?

If you look at the declaration of TIdIOHandler::ReadLn(), you will see that it has an optional ATerminator parameter:
String __fastcall ReadLn(_di_IIdTextEncoding AByteEncoding = NULL);
String __fastcall ReadLn(String ATerminator, _di_IIdTextEncoding AByteEncoding);
String __fastcall ReadLn(String ATerminator, int ATimeout = IdTimeoutDefault, int AMaxLineLength = -1, _di_IIdTextEncoding AByteEncoding = NULL);
If you don’t specify a terminator, the default is LF (which includes CRLF). You can specify a different terminator as desired, eg:
void __fastcall TForm1::IdTCPServer1Execute(TIdContext *AContext)
{
...
String mystring = AContext->Connection->IOHandler->ReadLn(_D(";"));
...
}
On a separate note, TIdTCPServer is a multi-threaded component. Its events are fired in the context of worker threads. As such, you MUST synchronize with the main UI thread when accessing UI controls, eg:
void __fastcall TForm1::AddLine(const String &S)
{
#if defined(__clang__)
TThread::Synchronize(nullptr, [&, this](){ RichEdit1->Lines->Add(S); });
#else
struct TAddLineSync
{
String Line;
TAddLineSync(const String &ALine) : Line(ALine) {}
void __fastcall AddLine() { Form1->RichEdit1->Lines->Add(Line); }
};
TAddLineSync sync(S);
TThread::Synchronize(NULL, &sync.AddLine);
#endif
}
//-----------------------------------
void __fastcall TForm1::IdTCPServer1Execute(TIdContext *AContext)
{
AddLine(_D(" * "));
String mystring = AContext->Connection->IOHandler->ReadLn(_D(";"));
AddLine(mystring + _D(" *** "));
}
//-----------------------------------
void __fastcall TForm1::IdTCPServer1Disconnect(TIdContext *AContext)
{
AddLine(_D("Disconnected. . . \r\n"));
}
//--------------------------------------
void __fastcall TForm1::IdTCPServer1Connect(TIdContext *AContext)
{
AddLine(_D("Connected. . . \r\n"));
}
//---------------------------------------

Related

store a lambda that captures this

Using C++ 17, I'm looking for a way to store a lambda that captures the this pointer, without using std::function<>. The reason to not using std::function<> is that I need the guaranty that no dynamic memory allocations are used. The purpose of this, is to be able to define some asynchronous program flow. Example:
class foo {
public:
void start() {
timer(1ms, [this](){
set_pin(1,2);
timer(1ms, [this](){
set_pin(2,1);
}
}
}
private:
template < class Timeout, class Callback >
void timer( Timeout to, Callback&& cb ) {
cb_ = cb;
// setup timer and call cb_ one timeout reached
...
}
??? cb_;
};
Edit: Maybe it's not really clear: std::function<void()> would do the job, but I need / like to have the guaranty, that no dynamic allocations happens as the project is in the embedded field. In practice std::function<void()> seems to not require dynamic memory allocation, if the lambda just captures this. I guess this is due to some small object optimizations, but I would like to not rely on that.
You can write your own function_lite to store the lambda, then you can use static_assert to check the size and alignment requirements are satisfied:
#include <cstddef>
#include <new>
#include <type_traits>
class function_lite {
static constexpr unsigned buffer_size = 16;
using trampoline_type = void (function_lite::*)() const;
trampoline_type trampoline;
trampoline_type cleanup;
alignas(std::max_align_t) char buffer[buffer_size];
template <typename T>
void trampoline_func() const {
auto const obj =
std::launder(static_cast<const T*>(static_cast<const void*>(buffer)));
(*obj)();
}
template <typename T>
void cleanup_func() const {
auto const obj =
std::launder(static_cast<const T*>(static_cast<const void*>(buffer)));
obj->~T();
}
public:
template <typename T>
function_lite(T t)
: trampoline(&function_lite::trampoline_func<T>),
cleanup(&function_lite::cleanup_func<T>) {
static_assert(sizeof(T) <= buffer_size);
static_assert(alignof(T) <= alignof(std::max_align_t));
new (static_cast<void*>(buffer)) T(t);
}
~function_lite() { (this->*cleanup)(); }
function_lite(function_lite const&) = delete;
function_lite& operator=(function_lite const&) = delete;
void operator()() const { (this->*trampoline)(); }
};
int main() {
int x = 0;
function_lite f([x] {});
}
Note: this is not copyable; to add copy or move semantics you will need to add new members like trampoline and cleanup which can properly copy the stored object.
There is no drop in replacement in the language or the standard library.
Every lambda is a unique type in the typesystem. Technically you may have a lambda as a member, but then its type is fixed. You may not assign other lambdas to it.
If you really want to have an owning function wrapper like std::function, you need to write your own. Actually you want a std::function with a big enough small-buffer-optimization buffer.
Another approach would be to omit the this capture and pass it to the function when doing the call. So you have a captureless lambda, which is convertible to a function pointer which you can easily store. I would take this route and adapter complexer ways if really nessessary.
it would look like this (i trimmed down the code a bit):
class foo
{
public:
void start()
{
timer(1, [](foo* instance)
{
instance->set_pin(1,2);
});
}
private:
template < class Timeout, class Callback >
void timer( Timeout to, Callback&& cb )
{
cb_ = cb;
cb_(this); // call the callback like this
}
void set_pin(int, int)
{
std::cout << "pin set\n";
}
void(*cb_)(foo*);
};

C++ builder (no idea what to do with this code)

Sorry if this is wrong place to ask. I have 2 pieces of code given to me:
IdHTTP1->Head("http://dsrt.dyndns.org/files/MAIN.zip");
Memo1->Lines->Add(DateTimeToStr(IdHTTP1->Response->Date));
Memo1->Lines->Add(DateTimeToStr(IdHTTP1->Response->Expires));
Memo1->Lines->Add(DateTimeToStr(IdHTTP1->Response->LastModified));
and also this:
TDateTime dt;
AnsiString str = DateToStr(dt);
IdHTTP1->Head("http://dsrt.dyndns.org/files/MAIN.zip");
if(DateToStr(IdHTTP1->Response->Date) != str)
Memo1->Lines->Add(DateTimeToStr(IdHTTP1->Response->Date));
if(DateToStr(IdHTTP1->Response->Expires) != str)
Memo1->Lines->Add(DateTimeToStr(IdHTTP1->Response->Expires));
if(DateToStr(IdHTTP1->Response->LastModified) != str)
Memo1->Lines->Add(DateTimeToStr(IdHTTP1->Response->LastModified));
Somehow it's supposed to return a date when file was uploaded. But I have no clue how to get c++ builder to compile it. I get syntax errors and "multiple memo1 something" error. Please help.
TMemo is an Embarcadero visual UI component. TIdHTTP is a component of the Indy Project, which ships preinstalled in Delphi and C++Builder.
The code you were given is UI-related code, so create UI for it. In the C++Builder IDE, create a new TForm class, drop a TMemo on it and name it Memo1, and drop a TIdHTTP component and name it IdHTTP1, and then use something like a button OnClick handler to invoke the HTTP code, eg:
Unit1.h:
//---------------------------------------------------------------------------
#ifndef Unit1H
#define Unit1H
//---------------------------------------------------------------------------
#include <System.Classes.hpp>
#include <Vcl.Controls.hpp>
#include <Vcl.StdCtrls.hpp>
#include <Vcl.Forms.hpp>
#include <IdHTTP.hpp>
//---------------------------------------------------------------------------
class TForm1 : public TForm
{
__published: // IDE-managed Components
TMemo *Memo1;
TButton *Button1;
TButton *Button2;
TIdHTTP *IdHTTP1;
void __fastcall Button1Click(TObject *Sender);
void __fastcall Button2Click(TObject *Sender);
private: // User declarations
public: // User declarations
__fastcall TForm1(TComponent* Owner);
};
//---------------------------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//---------------------------------------------------------------------------
#endif
Unit1.cpp:
//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
#include <System.Sysutils.hpp>
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
IdHTTP1->Head("http://dsrt.dyndns.org/files/MAIN.zip");
Memo1->Lines->Add(DateTimeToStr(IdHTTP1->Response->Date));
Memo1->Lines->Add(DateTimeToStr(IdHTTP1->Response->Expires));
Memo1->Lines->Add(DateTimeToStr(IdHTTP1->Response->LastModified));
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button2Click(TObject *Sender)
{
TDateTime dt = ...; // assign some value
// see functions such as Date(), Now(), EncodeDateTime(), etc,
// or use the TDateTimePicker component...
System::String str = DateToStr(dt);
IdHTTP1->Head("http://dsrt.dyndns.org/files/MAIN.zip");
if (DateToStr(IdHTTP1->Response->Date) != str)
Memo1->Lines->Add(DateTimeToStr(IdHTTP1->Response->Date));
if (DateToStr(IdHTTP1->Response->Expires) != str)
Memo1->Lines->Add(DateTimeToStr(IdHTTP1->Response->Expires));
if (DateToStr(IdHTTP1->Response->LastModified) != str)
Memo1->Lines->Add(DateTimeToStr(IdHTTP1->Response->LastModified));
}
//---------------------------------------------------------------------------
That being said, using strings to compare date/time values is not a good idea in general. Such strings are subject to locale issues. You are using conversion functions that are dependent on the local machine's current locale, not HTTP's standardized date/time formats. The TIdHTTP properties you are using are TDateTime values, where TIdHTTP has already translated the HTTP-provided values into binary values in local date/time (based on the local machine's current timezone). You can compare those values as-is without worrying about any string conversions, eg:
void __fastcall TForm1::Button2Click(TObject *Sender)
{
TDateTime dt = ...; // assign some value
// see functions such as Date(), Now(), EncodeDateTime(), etc,
// or use the TDateTimePicker component...
IdHTTP1->Head("http://dsrt.dyndns.org/files/MAIN.zip");
if (IdHTTP1->Response->Date != dt)
{
//...
}
if (IdHTTP1->Response->Expires != dt)
{
//...
}
if (IdHTTP1->Response->LastModified != dt)
{
//...
}
}
Doing TDateTime comparisons is much more accurate and reliable than string comparisons. And you are not limited to just the == and != operators, you can use the < and > operators as well:
void __fastcall TForm1::Button2Click(TObject *Sender)
{
TDateTime dt = Date();
IdHTTP1->Head("http://dsrt.dyndns.org/files/MAIN.zip");
if (IdHTTP1->Response->Date < dt)
{
// older than today...
}
if (IdHTTP1->Response->Expires < dt)
{
// expired prior to today...
}
if (IdHTTP1->Response->LastModified < dt)
{
// last modified prior to today...
}
}

How to put TEdit data into String C++ Builder

I am new in programming. Actually I am in 2nd year of college and starting an internship. They want me to do a program in C++ builder but I only know C. What I studied. And I don't have any knowledge about OOP.
So my question is.
I have TEdit1 and I want to verify if the data introduced in that textbox is a number. I know to verify if it is a number but I don't know how to put data from TEdit into string.
I wrote some code but it doesn't work.
void __fastcall TForm1::Button1Click(TObject *Sender)
{
int Size = Edit1->GetTextLen(); //Get length of string in Edit1
Size++; //Add room for null character
Char *Buffer = new Char[Size]; //Creates Buffer dynamic variable
std::auto_ptr<Char> Buffer(new Char[Size]);
Edit1->GetTextBuf(Buffer.get(),Size); //Puts Edit1->Text into Buffer
ShowMessage(Buffer);
}
And I get these errors:
E2034 Cannot convert 'std::auto_ptr<wchar_t>' to 'UnicodeString'
E2342 Type mismatch in parameter 'Msg' (wanted 'const UnicodeString', got std::auto_ptr<wchar_t>')
Can you please explain what i did wrong, or where i can found Embarcadero C++ Builder tutorials? I searched all google and didn't find something to help me.
Your code has several mistakes in it:
you are declaring two Buffer variables in the same scope. That is not allowed. You need to remove one of them.
you are passing the std::auto_ptr itself to ShowMessage(), but it expects a System::UnicodeString instead, thus the compiler error message. You can use the std::auto_ptr::get() method to get the wchar_t* pointer and pass it to ShowMessage(), as UnicodeString has a constructor that accepts wchar_t* as input:
ShowMessage(Buffer.get());
despite the above, you actually cannot use a pointer from new[] with std::auto_ptr to begin with. std::auto_ptr uses delete instead of delete[] to free the memory being pointed at. You must always use delete with new, and delete[] with new[]. So, while the code will compile, it will not free the memory correctly at runtime. C++11 introduced a new std::unique_ptr class to replace std::auto_ptr, and std::unique_ptr supports new[] and delete[] (however, C++Builder's 32bit compiler does not support C++11 yet - that is in the works - but its 64bit compiler does):
#include <memory>
void __fastcall TForm1::Button1Click(TObject *Sender)
{
int Size = Edit1->GetTextLen(); //Get length of string in Edit1
Size++; //Add room for null character
std::unique_ptr<Char[]> Buffer(new Char[Size]); //Creates Buffer dynamic variable
Edit1->GetTextBuf(Buffer.get(), Size); //Puts Edit1->Text into Buffer
ShowMessage(Buffer.get());
}
Now, with that said, if you are going to continue using C++Builder, you should learn how to use its built-in functionalities, like UnicodeString, which the RTL/VCL relies heavily on (use the System::String alias for most code, use UnicodeString directly only when absolutely necessary).
Your example can be vastly simplified using the TEdit::Text property:
void __fastcall TForm1::Button1Click(TObject *Sender)
{
String s = Edit1->Text; //Get string in Edit1
ShowMessage(s);
}
The simplest solution to your problem would be to use the TCSpinEdit component instead of TEdit, as TCSpinEdit only allows numeric input in the first place, and has a Value property that returns an int:
void __fastcall TForm1::Button1Click(TObject *Sender)
{
int number = CSpinEdit1->Value;
ShowMessage("It is a number");
// use number as needed...
}
But, if you have to stick with TEdit, there are many ways to check a UnicodeString for numeric content.
You can set the TEdit::NumbersOnly property to true so the user cannot enter a non-numeric value (unless they use copy/paste, but let's ignore that for the moment), and then use the RTL's StrToInt() function, or System::UnicodeString::ToInt() method, to convert it as-is:
#include <System.SysUtils.hpp>
void __fastcall TForm1::Button1Click(TObject *Sender)
{
int number;
try
{
number = StrToInt(Edit1->Text);
// or: number = Edit1->Text.ToInt();
}
catch (const EConvertError&)
{
// not a number, do something else...
ShowMessage("It is not a number");
return;
}
ShowMessage("It is a number");
// use number as needed...
}
Or you can use the RTL's System::Sysutils::TryStrToInt() function:
#include <System.SysUtils.hpp>
void __fastcall TForm1::Button1Click(TObject *Sender)
{
int number;
if (TryStrToInt(Edit1->Text, number))
{
// use number as needed...
ShowMessage("It is a number");
}
else
{
// not a number, do something else...
ShowMessage("It is not a number");
}
}
Or you can use the STL's std::wistringstream class:
#include <sstream>
void __fastcall TForm1::Button1Click(TObject *Sender)
{
int number;
std::wistringstream iss(Edit1->Text.c_str());
if (iss >> number)
{
// use number as needed...
ShowMessage("It is a number");
}
else
{
// not a number, do something else...
ShowMessage("It is not a number");
}
}
Or, since you have a C background, you can use the C _wtoi() function (which doesn't offer much in the way of error checking):
#include <cstdlib>
void __fastcall TForm1::Button1Click(TObject *Sender)
{
int number = std::_wtoi(Edit1->Text.c_str());
if (number != 0)
{
// use number as needed...
ShowMessage("It is a valid number");
}
else
{
// not a number, do something else...
ShowMessage("It is not a valid number");
}
}
Or you can use the C wcstol() function:
#include <cstdlib>
void __fastcall TForm1::Button1Click(TObject *Sender)
{
String s = Edit1->Text;
Char *p = s.c_str(), *end;
int number = std::wcstol(p, &end, 10);
if (end != p)
{
// use number as needed...
ShowMessage("It is a number");
}
else
{
// not a number, do something else...
ShowMessage("It is not a number");
}
}
Or you can use the C swscanf() function:
#include <cstdio>
void __fastcall TForm1::Button1Click(TObject *Sender)
{
int number;
if (std::swscanf(Edit1->Text.c_str(), L"%d", &number) == 1)
{
// use number as needed...
ShowMessage("It is a number");
}
else
{
// not a number, do something else...
ShowMessage("It is not a number");
}
}

How to read in 8 bytes of data from a DataInputStream and interpreted it as double in Vala

I looking for the equivalent of
java.io.DataInputStream.readDouble() for Vala.
Is it even possible?
Currently I have :
public double data;
public override void load (DataInputStream dis) throws IOError {
data = dis.read_int64 ();
}
But it just converting a int64 to a double which is not what I want.
I've tried all sort of casting and de-referencing, but nothing seems to work.
This worked for me:
int main()
{
int64 foo = 0; // Whatever value you have
double data = *(double*)(&foo); // This is where the "magic" happens
stdout.printf("%f", data);
return 0;
}
Mind you, you may have to set the correct byte order for the conversion to succeed.

How to pass and return a TStream using DataSnap in C++ Builder

I have seen lots of DataSnap examples in Delphi, but fewer in C++ Builder, and have not figured out how to specify that a TStream should be returned to the calling client.
I am using a simple configuration, similar to the tutorials I have seen. An example server method is:
System::UnicodeString GetData(int PatientID, int& count, TStream* stream);
I have no trouble calling that method from my client. Because count is passed as a reference, the DataSnap server knows to send it back to the client. Generate Client Classes, on the TSQLConnection in the client, connects to the server, and generates the following:
System::UnicodeString __fastcall TServerMethods1Client::GetData(int PatientID, int &count, TStream* stream)
{
if (FGetDataCommand == NULL)
{
FGetDataCommand = FDBXConnection->CreateCommand();
FGetDataCommand->CommandType = TDBXCommandTypes_DSServerMethod;
FGetDataCommand->Text = "TServerMethods1.GetData";
FGetDataCommand->Prepare();
}
FGetDataCommand->Parameters->Parameter[0]->Value->SetInt32(PatientID);
FGetDataCommand->Parameters->Parameter[1]->Value->SetInt32(count);
FGetDataCommand->Parameters->Parameter[2]->Value->SetStream(stream, FInstanceOwner);
FGetDataCommand->ExecuteUpdate();
count = FGetDataCommand->Parameters->Parameter[1]->Value->GetInt32();
System::UnicodeString result = FGetDataCommand->Parameters->Parameter[3]->Value->GetWideString();
return result;
}
One can see that the generated code is setting the count from the returned parameter, indicating that the server is sending it back. However, the stream is only sent to the server, and not set back on the client.
In Delphi, I would use var to indicate that the reference should be passed back to the caller. However, using a reference on TStream does not work, either.
For this definition:
System::UnicodeString GetData(int PatientID, int& count, TStream& stream);
I get this generated code:
System::UnicodeString __fastcall TServerMethods1Client::GetData(int PatientID, int &count, TStream* &stream)
{
if (FGetDataCommand == NULL)
{
FGetDataCommand = FDBXConnection->CreateCommand();
FGetDataCommand->CommandType = TDBXCommandTypes_DSServerMethod;
FGetDataCommand->Text = "TServerMethods1.GetData";
FGetDataCommand->Prepare();
}
FGetDataCommand->Parameters->Parameter[0]->Value->SetInt32(PatientID);
FGetDataCommand->Parameters->Parameter[1]->Value->SetInt32(count);
FGetDataCommand->Parameters->Parameter[2]->Value->SetStream(stream, FInstanceOwner);
FGetDataCommand->ExecuteUpdate();
count = FGetDataCommand->Parameters->Parameter[1]->Value->GetInt32();
stream = FGetDataCommand->Parameters->Parameter[2]->Value->GetStream(FInstanceOwner);
System::UnicodeString result = FGetDataCommand->Parameters->Parameter[3]->Value->GetWideString();
return result;
}
Which throws an access violation in the ExecuteUpdate() call.
Is there a way I can pass a pointer to the server method and mark it in some way that the stream should be passed back to the calling client?
Remy's response was correct.
The server method is
System::UnicodeString TServerMethods1::GetData(int PatientID, int& count,
TStream*& stream) {
count = 10;
String newSimpleString = "New Simple String";
TByteDynArray theBytes;
theBytes.Length = newSimpleString.Length();
for (int i = 0; i < newSimpleString.Length(); i++) {
theBytes[i] = newSimpleString[i + 1];
}
TDBXBytesStream* newStream =
new TDBXBytesStream(theBytes, newSimpleString.Length());
stream = newStream;
return "StringResult";
}
and the client method is
void __fastcall TForm1::Button2Click(TObject *Sender) {
int count = 1;
TStringStream* stringStream = new TStringStream(String("Passed In"));
TStream* str = stringStream;
String result = ClientModule1->ServerMethods1Client->GetData(1, count, str);
int len = str->Size;
ShowMessage("Result is: '" + result + "' and " + String(count) +
", and stream is " + String(len) + " bytes long");
}
With FInstanceOwner at true (the default as generated), the stream passed in as a client method gets freed the next time the method is called.
On the server, the newly created TDBXBytesStream gets freed the next time the server method is called.
TDBXBytesStream is not a fully functional stream - for example, I could not get CopyFrom to copy from a TStringStream (for example), which is why I use the TByteDynArray.
Also, I could not modify the stream passed in to the server method by writing to it, which is why I needed to assign a new TDBXBytesStream to the passed in reference.
Thanks, Remy, for helping me with this.

Resources