FMX System messaging to send a pointer(?) - android

I'm using C++Builder 10.2.
In Android, I would like to send messages from various threads, including the main thread, to the main GUI thread. In Windows, I could post a message and assign an LPARAM or WPARAM to the address of some instance of a struct or class.
I'm trying to use System.Messaging.TMessageManager to do the same thing, similar to the example here: System.Messaging (C++). But I can only send 'simple' types, like UnicodeString or int. I haven't worked out how to send a pointer, assuming it's even possible at all.
I would like to send a struct/class instance like this:
class TSendResult
{
public:
String Message;
unsigned int Value;
int Errno;
__fastcall TSendResult(void);
__fastcall ~TSendResult();
};
If this can be done, how do I write this? I managed to get one version to compile, but got a linker error:
error: undefined reference to 'vtable for System::Messaging::TMessage__1<TSendResult>'
Form constructor:
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
TMessageManager* MessageManager = TMessageManager::DefaultManager;
TMetaClass* MessageClass = __classid(TMessage__1<TSendResult>);
TMessageListenerMethod ShowReceivedMessagePointer = &(this->MMReceiveAndCallBack);
MessageManager->SubscribeToMessage(MessageClass, ShowReceivedMessagePointer);
}
Button click handler:
void __fastcall TForm1::SpeedButton1Click(TObject *Sender)
{
...
TSendResult *SPtr = new TSendResult();
SPtr->Message = "All good";
SPtr->Value = 10;
SPtr->Errno = 0;
TMessageManager* MessageManager = TMessageManager::DefaultManager;
TMessage__1<TSendResult>* Message = new TMessage__1<TSendResult>(*SPtr); // <-- this doesn't look right...
MessageManager->SendMessage(Sender, Message, false);
}
Function that captures messages:
void __fastcall TForm1::MMReceiveAndCallBack(System::TObject* const Sender,
System::Messaging::TMessageBase* const M)
{
TMessage__1<TSendResult>* Message = dynamic_cast<TMessage__1<TSendResult>*>(M);
if (Message) {
ShowMessage(Message->Value.Message);
}
}

TMessage__1<T> is a C++ class implementation for the Delphi Generic TMessage<T> class. Unfortunately, there is a documented limitation when using Delphi Generic classes in C++, which is why you are getting a linker error:
How to Handle Delphi Generics in C++
Delphi generics are exposed to C++ as templates. However, it is important to realize that the instantiations occur on the Delphi side, not in C++. Therefore, you can only use these template for types that were explicitly instantiated in Delphi code.
...
If C++ code attempts to use a Delphi generic for types that were not instantiated in Delphi, you'll get errors at link time.
Which is why TMessage__1<UnicodeString> works but TMessage__1<TSendResult> does not, as there is an instantiation of TMessage<UnicodeString> present in the Delphi RTL. Whoever wrote the C++ example you are looking at was likely not aware of this limitation and was just translating the Delphi example as-is.
That being said, you have two choices:
Add a Delphi .pas unit to your C++ project, implementing TSendResult as a Delphi record, and defining an instantiation of TMessage<TSendResult> for it. Then you can use that unit in your C++ code (C++Builder will generate a C++ .hpp file for you when the .pas file is compiled), eg:
unit MyMessageTypes;
interface
uses
System.Messaging;
type
TSendResult = record
Message: String;
Value: UInt32;
Errno: Integer;
end;
TSendResultMsg = TMessage<TSendResult>;
implementation
initialization
TSendResultMsg.Create.Free;
finalization
end.
#include "MyMessageTypes.hpp"
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
TMessageManager::DefaultManager->SubscribeToMessage(__classid(TSendResultMsg), &MMReceiveAndCallBack);
}
void __fastcall TForm1::SpeedButton1Click(TObject *Sender)
{
...
TSendResult Res;
Res.Message = _D("All good");
Res.Value = 10;
Res.Errno = 0;
TSendResultMsg *Message = new TSendResultMsg(Res);
TMessageManager::DefaultManager->SendMessage(this, Message, true);
}
void __fastcall TForm1::MMReceiveAndCallBack(System::TObject* const Sender,
System::Messaging::TMessageBase* const M)
{
const TSendResultMsg* Message = static_cast<const TSendResultMsg*>(M);
ShowMessage(Message->Value.Message);
}
rather than using TMessage__1 at all, you can instead derive TSendResult directly from TMessageBase, eg:
class TSendResultMsg : public TMessageBase
{
public:
String Message;
unsigned int Value;
int Errno;
};
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
TMessageManager::DefaultManager->SubscribeToMessage(__classid(TSendResultMsg), &MMReceiveAndCallBack);
}
void __fastcall TForm1::SpeedButton1Click(TObject *Sender)
{
...
TSendResultMsg *Message = new TSendResultMsg;
Message->Message = _D("All good");
Message->Value = 10;
Message->Errno = 0;
TMessageManager::DefaultManager->SendMessage(this, Message, true);
}
void __fastcall TForm1::MMReceiveAndCallBack(System::TObject* const Sender,
System::Messaging::TMessageBase* const M)
{
const TSendResultMsg* Message = static_cast<const TSendResultMsg*>(M);
ShowMessage(Message->Message);
}

Related

Firemonkey TSpinBox height

I'm using C++ Builder 10.3 and my application is for Android, please note I'm very new to C++ Builder
I'm trying to change the font size and height of a TSpinBox but i'm unable to change the height.
I tried by best to port the following Delphi solution
Firemonkey TEdit height but with no joy and i'm a total lose.
AdjustFixedSize is declared private i dont think its being overridden, i have also tried creating a setter and calling it but yet again I was unable to get it to work. The biggest problem i have is my lack of C++ Builder knowledge.
Header
class TMySpinBox : public TSpinBox{
public:
protected:
virtual void AdjustFixedSize(const TControl Ref) ;
};
CPP
TMySpinBox::TMySpinBox() : TSpinBox(0){};
void TMySpinBox::AdjustFixedSize(const TControl Ref){
SetAdjustType(TAdjustType::None);
Code
TMySpinBox* SpinBox1 = new TMySpinBox();
SpinBox1->ControlType=TControlType::Platform;
SpinBox1->Parent=Panel1->Parent;
SpinBox1->Position->Y=16.0;
SpinBox1->Position->X=16.0;
SpinBox1->Min=2;
SpinBox1->Max=99;
SpinBox1->Font->Size=48;
SpinBox1->Visible=true;
SpinBox1->Value=2;
SpinBox1->Align=TAlignLayout::None;
SpinBox1->Height=100;
Width=100;
I gave it a try and moved a few things around - mostly into the constructor of the customized TSpinBox. I skipped using AdjustFixedSize since it doesn't seem necessary.
myspinbox.h
#ifndef myspinboxH
#define myspinboxH
//---------------------------------------------------------------------------
#include <FMX.SpinBox.hpp>
class TMySpinBox : public TSpinBox {
protected:
// The correct signature but commented out since I didn't use it:
//void __fastcall AdjustFixedSize(TControl* const ReferenceControl) override;
public:
// C++ Builder constructors can be virtual and override which is not
// standard C++. This is afaik only important if you make a custom component
// to integrate with the IDE to support streaming it, but I'll make it
// virtual anyway.
// This component sets Owner and Parent to the same component. You can change that if
// you'd like to keep them separate.
virtual __fastcall TMySpinBox(Fmx::Types::TFmxObject* OwnerAndParent);
};
#endif
myspinbox.cpp
#pragma hdrstop
#include "myspinbox.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
__fastcall TMySpinBox::TMySpinBox(Fmx::Types::TFmxObject* OwnerAndParent) :
TSpinBox(OwnerAndParent) // set owner
{
// set properties
this->Parent = OwnerAndParent;
this->Position->Y = 16.0;
this->Position->X = 16.0;
this->Min = 2;
this->Max = 99;
this->Value = this->Min;
this->Height = 100;
this->Width = 100;
// Remove the styled setting for Size to enable setting our own font size
this->StyledSettings >>= Fmx::Types::TStyledSetting::Size;
this->Font->Size = 48;
}
Code
// Let Panel1 own and contain the spinbox and destroy it when it itself is destroyed
TMySpinBox* SpinBox1 = new TMySpinBox(Panel1);
Disclaimer: Only tested on Windows

Why imbue with boost::posix_time::time_facet with std::cout redirection ends up throwing bad_cast?

I have a test program, based on CPPUNIT library and Qt that runs ~900 unit tests. This program is deployed on Android using QtCreator. It links with ~80 libraries, each one defining some tests.
After a code change, test program started crashing due to std::bad_cast being thrown.
Original code (no bad_cast):
class TimeUnit
{
friend std::ostream& operator<<(std::ostream &os, const TimeUnit& var);
public:
std::string TimeUnit::toString() const
{
std::stringstream stream;
static std::string facetStr = "%Y/%m/%d %H:%M";
stream.imbue(std::locale(std::locale::classic(), new boost::posix_time::time_facet(facetStr.c_str())));
stream << *m_time;
return stream.str();
}
private:
boost::posix_time::ptime m_time;
};
std::ostream& operator<<(std::ostream &os, const TimeUnit& var)
{
os << var.toString();
return os;
}
New code (makes the test program throw bad_cast at some point):
class TimeUnit
{
friend std::ostream& operator<<(std::ostream &os, const TimeUnit& var);
public:
std::string TimeUnit::toString() const
{
std::stringstream stream;
stream << *this;
return stream.str();
}
private:
boost::posix_time::ptime m_time;
};
std::ostream& operator<<(std::ostream &os, const TimeUnit& var)
{
static std::string facetStr = "%Y/%m/%d %H:%M";
os.imbue(std::locale(std::locale::classic(), new boost::posix_time::time_facet(facetStr.c_str())));
// this next line ends up throwing std::bad_cast!
os << *(var.m_time);
return os;
}
The new code makes std::bad_cast exception be thrown by my operator<< at some point. According to the doc:
An exception of this type is thrown when a dynamic_cast to a reference
type fails the run-time check (e.g. because the types are not related
by inheritance), and also from std::use_facet if the requested facet
does not exist in the locale.
...boost operator<< for boost::posix_time::ptime does a use_facet...
After a lot of investigation, I can conclude that:
The crash is Android-specific (Window works just prefectly)
It is due to attaching a facet to std::cout (in the new code, if I create a local std::stringstream apply the facet to it and later copy the result with os << str.str(), no more crash)
os.imbue(std::locale(std::locale::classic(),...) or os.imbue(std::locale(os.getloc(),...)) both will lead to the same crash
Removing os.imbue call makes the crash disappear
Calling os.imbue(std::locale::classic()); before returning from my operator<< does not fix the issue
Unfortunately, I tried to isolate the problem in a MCVE but could not reproduce the problem with some more basic code...
So my question is (are):
Am I doing something wrong in my operator<<?
Why would this code work on Windows and crash on Android? Could this be due to an insane C++ runtime that would delete the facet in a wrong way?
Edit:
After some more investigation, I found out that the bad_cast is only thrown if I redirect std::cout to a file from my main, using:
class ScopedRedirect
{
public:
ScopedRedirect(std::ostream & inOriginal, std::ostream & inRedirect) :
mOriginal(inOriginal),
mOldBuffer(inOriginal.rdbuf(inRedirect.rdbuf()))
{}
virtual ~ScopedRedirect()
{
mOriginal.rdbuf(mOldBuffer);
}
ScopedRedirect(const ScopedRedirect&) = delete;
ScopedRedirect& operator=(const ScopedRedirect&) = delete;
protected:
std::ostream & mOriginal;
std::streambuf * mOldBuffer;
};
int main( int argc, char* argv[] )
{
std::fstream output( "cout.txt", std::ios_base::out );
assert( output.is_open() );
ScopedRedirect redirectCount( std::cout, output );
...
}

Android native C function as variable?

So, I'm working with a library that uses a callback function that is configured and called when it's needed. I need to access local variables in my c function from inside that function and can't make them members of the parent class for other reasons.
So, essentially this is my set up
callback.h
typedef void handler_func(uint8_t *data, size_t len);
typedef struct my_cfg {
handler_func *handler;
} my_cfg;
otherfile.c
#include "callback.h"
void test() {
char *test = "This is a test";
my_cfg cfg = { 0 };
memset(&cfg, 0, sizeof(my_cfg));
my_cfg.handler = my_handler;
// This is just an example, basically
// elsewhere in the code the handler
// function will be called when needed.
load_config(my_cfg);
}
void my_handler(uint8_t *data, size_t len) {
// I need to access the `test` var here.
}
What I need is something like this:
#include "callback.h"
void test() {
final char *test = "This is a test";
my_cfg cfg = { 0 };
memset(&cfg, 0, sizeof(my_cfg));
// This is the type of functionality I need.
my_cfg.handler = void (uint8_t *data, size_t len) {
printf("I can now access test! %s", test);
};
// This is just an example, basically
// elsewhere in the code the handler
// function will be called when needed.
load_config(my_cfg);
}
Please keep in mind that I cannot change the header files that define the function definition for handler_func, nor can I modify the my_cfg struct, nor can I modify the area of the code that is calling the handler_func, my_cfg.handler. They are all internal in the library.
(Also note that there may be code errors above, this is all psuedo code technically. I'm not at my computer, just typing this all out free hand on a tablet)
Edit
From what I understand, nested functions would solve this issue. But it appears that clang doesn't support nested functions.
Reference: https://clang.llvm.org/docs/UsersManual.html#gcc-extensions-not-implemented-yet
clang does not support nested functions; this is a complex feature
which is infrequently used, so it is unlikely to be implemented
anytime soon.
Is there another work around?

Why can't my C code find my Java functions?

My application makes calls to a native library, and then within the native library, makes calls back to the Java code. I have the calls TO the native library working correctly ( I'm pretty sure ), but not its telling me that it can't find the Java functions when trying to call them from the C file.
The application doesn't even crash, there is no pop up telling me it closed unexpectedly, it just flashes a black screen and nothing happens.
The code I am trying to reproduce was originally made in C for Palm Pilot, and is being transferred to an Android device. This is the original call in C I am trying to duplicate...
InitRelay(Changeit,getit,putit,flushit,delayit);
The parameters in the call above are function names, the functions are here below:
void Changeit(WORD baud){
ChangeRate(baud);
}
extern Boolean ChangeRate(UInt32 baud)
{...}
Int16 getit(UInt16 t) {...}
void putit( BYTE p ) {...}
void flushit(void){...}
extern void delayit( UInt16 wait ) {...}
This is InitRelay shown in my .c file:
BYTE __stdcall InitRelay(fp_setbaud _setbaud, fp_get _get, fp_put _put, fp_flush _flush, fp_delay _delay){
RelayAPI_SetBaud=_setbaud;
RelayAPI_get=_get;
RelayAPI_put=_put;
RelayAPI_flush=_flush;
RelayAPI_delay=_delay;
....
}
The parameters shown in above are typedefed in my .h file:
typedef void (__stdcall *fp_setbaud)(WORD);
typedef short (__stdcall *fp_get)(WORD);
typedef void (__stdcall *fp_put)(BYTE);
typedef void (__stdcall *fp_flush)(void);
typedef void (__stdcall *fp_delay)(WORD);
The WORD/BYTE/DWORD types are defined in a separate .h file shown here:
typedef unsigned char BYTE;
typedef unsigned short WORD;
typedef unsigned long DWORD;
Now, in my Android code, I call a function I made called InitRelayJava(), all of my data for the application is stored in a separate class called RelayAPIModel. I created a nested class within that to store all of my native functions. I did this so that I can access these functions the same way no matter what Activity the application is currently in.
public class RelayAPIModel {
....
public static class NativeCalls {
static {
System.loadLibrary( "RelayAPI" );
}
public native static byte InitRelayJava();
public native static void FreeRelayJava();
public static void changeItJavaWrapper( short l ) {
mModelService.changeitJava( l );
}
public static void flushItJavaWrapper() {
mModelService.flushitJava();
}
public static void putItJavaWrapper( byte[] p ) {
mModelService.putitJava( p );
}
public static void delayItJavaWrapper( short wait ) {
mModelService.delayitJava( wait );
}
public static short getItJavaWrapper( short s ) {
return mModelService.getitJava( s );
}
}
}
The calls made inside the *Wrapper functions go to a separate class that I have to handle all of the applications bluetooth capabilities. I do not think that those are needed for this problem.
This is what InitRelayJava looks like in my C code...
BYTE Java_my_eti_commander_RelayAPIModel_00024NativeCalls_InitRelayJava( JNIEnv *env, jobject obj ) {
myEnv = (env);
bluetoothClass = (*env)->GetObjectClass( env, obj );
myObject = obj;
changeID = (*myEnv)->GetMethodID( myEnv, myObject, "changeitJavaWrapper", "(S)V" );
getID = (*myEnv)->GetMethodID( myEnv, myObject, "getitJavaWrapper" , "(S)S" );
putID = (*myEnv)->GetMethodID( myEnv, myObject, "putitJavaWrapper" , "(B)V" );
flushID = (*myEnv)->GetMethodID( myEnv, myObject, "flushitJavaWrapper" , "()V" );
delayID = (*myEnv)->GetMethodID( myEnv, myObject, "delayitJavaWrapper" , "(S)V" );
...
}
This is the LogCat I am receiving...
08-02 10:27:32.406: D/dalvikvm(28376): Trying to load lib /data/data/my.eti.commander/lib/libRelayAPI.so 0x40515430
08-02 10:27:32.406: D/dalvikvm(28376): Added shared lib /data/data/my.eti.commander/lib/libRelayAPI.so 0x40515430
08-02 10:27:32.406: D/dalvikvm(28376): No JNI_OnLoad found in /data/data/my.eti.commander/lib/libRelayAPI.so 0x40515430, skipping init
08-02 10:27:32.406: D/dalvikvm(28376): GetMethodID: method not found: Lmy/eti/commander/RelayAPIModel$NativeCalls;.changeitJavaWrapper:(S)V
08-02 10:27:32.413: D/dalvikvm(28376): GetMethodID: method not found: Lmy/eti/commander/RelayAPIModel$NativeCalls;.getitJavaWrapper:(S)S
08-02 10:27:32.413: E/dalvikvm(28376): Class lookup Ljava/lang/NoSuchMethodError; attempted while exception Ljava/lang/NoSuchMethodError; pending
InitRelayJava is a static method - that means your second parameter (obj) is a class object pointer, not a this pointer. So get rid of the following line:
bluetoothClass = (*env)->GetObjectClass( env, obj );
and instead pass obj to GetMethodID(), like this:
changeID = (*myEnv)->GetMethodID( myEnv, obj, "changeitJavaWrapper", "(I)Z" );
EDIT: also, your parameter/return type signatures are wrong. Short is not the same as int, so the signature for changeitJavaWrapper is "(S)Z", for getitJavaWrapper is "()I" as it takes no parameters and returns an int. Be more careful please; it does not take an advanced knowledge of C to get those right, just some self-checking. This project of yours is inching into What have you tried? territory.
Cheat sheet on JNI type codes here.
Let me try and anticipate your next question - you cannot call those methods. Of course you cannot, it's not possible to call a nonstatic method from a static one. Your Java callbacks are all nonstatic, while the native method is static. Either make them static, or make the native method nonstatic and insert the GetObjectClass back.
EDIT2: so you changed your Java methods to static without telling. Now instead of (*env)->GetMethodID() you need to call (*env)->GetStaticMethodID() to get the method ID. Same parameters.
Your method signatures are wrong. Use javap -s classname to get the signatures of the class, for example: javap -s java.lang.String.
You can find javap in your local JDK.

N-Best support for pocketsphinx Android!

I have a research project in which i need n-best support in pocketsphinx android. I am using swig command line tool to generate pocketsphinx_wrap.c , and then NDK-build to generate shared library for android. The only problem is writing n-best content required in the pocketsphinx.i. Can any one please advise or guide me how to write function in pocketsphinx.i?
You don't write the function but you write wrapper, it's a very different thing. We already discussed with you that in the forum thread here:
https://sourceforge.net/projects/cmusphinx/forums/forum/5471/topic/4566470
The wrapper should look like this:
typedef struct ps_nbest_s NBest;
typedef struct ps_nbest_t {
} Nbest;
%extend Nbest {
Nbest(Decoder *d) {
Nbest *nbest = ps_nbest(d, 0, -1, NULL, NULL);
return nbest;
}
~Nbest() {
ps_nbest_free($self);
}
void next() {
ps_nbest_next($self);
}
Hypothesis* hyp() {
const char* hyp;
int32 score;
hyp = ps_nbest_hyp($self, &score);
return new_Hypothesis(hyp, "", score);
}
};

Categories

Resources