I have a method which builds a help message and then sets a TLabel's text property to that help message. However, whenever I attempt to set the text of the label, I get the following exception:
'String index out of range. (-1) Must be >=0 and <=42'
The method is as follows:
void __fastcall TPasswordChangeForm::BuildHelpMessage()
{
String HelpMsg = "";
if( NewPassEdit->Text.Length() < MinPasswordLength )
{
HelpMsg += "Password length too short.";
}
else
{
HelpMsg += "Password length OK.";
}
HelpMsg += "\n";
if( NewPassEdit->Text == ConfirmPassEdit->Text )
{
HelpMsg += "Passwords match.";
}
else
{
HelpMsg += "Passwords do not match.";
}
ShowMessage( HelpMsg ); //added for debugging, shows string as expected
HelpLabel->Text = HelpMsg; //exception thrown here
}
I added a ShowMessage call just to check the value of my string. It shows up just fine. I am also able to set the label to be any arbitrary value such as:
HelpLabel->Text = "This message works!";
Am I doing something wrong as I build the HelpMsg String?
Edit: Commenting out the line which adds the \n to the String fixes the problem. Similarly, the following code will cuase the exception:
String test = "this is a test";
test += "\n";
test += "test 2";
HelpLabel->Text = test;
What is it about the \n that causes issues? How do I correctly add a new line?
Currently updating to new new C++ Builder so cant play around with your snippet. I have method's for handling error messages which are output to a log tab as opposed to the ShowMessage, rather than using a string though I use a TStringList. For example:
void __fastcall TPasswordChangeForm::BuildHelpMessage()
{
TStringList HelpMsg = new TStringList(this);
if( NewPassEdit->Text.Length() < MinPasswordLength )
{
HelpMsg->Add("Password length too short.");
}
else
{
HelpMsg->Add("Password length OK.");
}
if( NewPassEdit->Text == ConfirmPassEdit->Text )
{
HelpMsg->Add("Passwords match.");
}
else
{
HelpMsg->Add("Passwords do not match."_;
}
ShowMessage(HelpMsg->Text); //added for debugging, shows string as expected
HelpLabel->Text = HelpMsg->Text; //exception thrown here
}
By using TStringList or TStrings (parent) when you access the Text property the strings within the object are output, each separated by a carriage return and line feed.
See the TStringList Docs here - hope this is of some help!
Related
I'm working on this project where I set up some commands that my TelegramBot can execute. And I would like to add an error message if the command written is wrong. Here is the code:
void handleNewMessages(int numNewMessages){
Serial.print("Handle New Messages: ");
Serial.println(numNewMessages);
for (int i = 0; i < numNewMessages; i++){
// Chat id of the requester
String chat_id = String(bot.messages[i].chat_id);
if (chat_id != chatId){
bot.sendMessage(chat_id, "Unauthorized user", "");
continue;
}
// Print the received message
String text = bot.messages[i].text;
Serial.println(text);
String fromName = bot.messages[i].from_name;
if (text == "/FlashOn") {
flashState = HIGH;
digitalWrite(LED, flashState);
}
if (text == "/FlashOff") {
flashState = LOW;
digitalWrite(LED, flashState);
}
if (text == "/photo") {
sendPhoto = true;
Serial.println("New photo request");
}
if (text == "/PIRON"){
PirControler = 1;
bot.sendMessage(chatId, "PIR Sensor is ON, you will get a notification if a motion is detected.", "");
}
if (text == "/PIROFF"){
PirControler = 0;
bot.sendMessage(chatId, "PIR sensor is OFF, you will no longer receive any notification.", "");
if (text == "/start"){
String welcome = "Hi sir, here are the commands I can execute for you :\n";
welcome += "/Photo : Take a new photo.\n";
welcome += "/FlashOn : Turns LED On.\n";
welcome += "/FlashOff : Turns LED off\n";
welcome += "/PIRON : Activate your PIR sensor.\n";
welcome += "/PIROFF : Shut your PIR sensor down.\n";
// welcome += "/readings : request sensor readings\n\n";
welcome += "You'll receive a photo whenever motion is detected.\n";
bot.sendMessage(chatId, welcome, "Markdown");
}
/*else {
bot.sendMessage(chatId, "I don't understand, sorry. Refer to the commands I showed you above.", "");
} **(Here is the message that I'd like to add)**
}
}
However, by trying to add the last line,
/*else {
bot.sendMessage(chatId, "I don't understand, sorry. Refer to the commands I showed you above.", "");
}*/
It returns an error to all the right commands except for the "/start" one.
Any ideas on how I could do that would be much appreciated :)
Let's make the example smaller
if (text == "/FlashOn") {
do stuff
}
if (text == "/start"){
do stuff
}
else {
report error
}
This code will always test if (text == "/start") regardless of the outcome of if (text == "/FlashOn") and if text is "/FlashOn", it cannot be "/start" and will execute the else and print the error message.
Solution
if (text == "/FlashOn") {
do stuff
}
else if (text == "/start"){
do stuff
}
else {
report error
}
Now if (text == "/start") will not be tested and the else case will not be run if text == "/FlashOn".
What this looks like if fully indented and bracketed:
if (text == "/FlashOn") {
do stuff
}
else {
if (text == "/start"){
do stuff
}
else {
report error
}
}
I have a TADOConnection pointing to a MySQL 8.0 instance. The connection is tested and it works. Following this example on how to use prepared statement, I'm having an error and I have no idea why.
The following code works fine, it will return true from the very last statement. No errors, no warnings.
AnsiString sqlQuery = "SELECT e.name FROM employee e WHERE e.id = 1;";
if (!_query->Connection->Connected) {
try {
_query->Connection->Connected = true;
} catch (EADOError& e) {
return false;
}
}
_query->SQL->Clear();
_query->SQL->Add(sqlQuery);
_query->Prepared = true;
try {
_query->Active = true;
if (_query->RecordCount == 0) {
return false;
}
} catch (EADOError& e) {
return false;
}
return true;
However, the following code fails executing _query->SQL->Add(sqlQuery); with this error:
Arguments are of the wrong type, are out of acceptable range, or are in conflict with one another.
AnsiString sqlQuery = "SELECT e.name FROM employee e WHERE e.id = :id;";
if (!_query->Connection->Connected) {
try {
_query->Connection->Connected = true;
} catch (EADOError& e) {
return false;
}
}
_query->SQL->Clear();
_query->SQL->Add(sqlQuery); // <---- EOleException here
_query->Parameters->ParamByName("id")->Value = id;
_query->Prepared = true;
try {
_query->Active = true;
if (_query->RecordCount == 0) {
return false;
}
} catch (EADOError& e) {
return false;
}
return true;
Everywhere I find examples, all of them use :paramName to specify parameters. What am I missing?
Update 1
I have tried changing the code like this :
_query->SQL->Clear();
TParameter * param = _query->Parameters->AddParameter();
param->Name = "id";
param->Value = 1;
_query->SQL->Add(sqlQuery); // <---- EOleException still here
Some forum post suggests to switch the Advanced Compiler option "Register Variables" to "None", but this is already the setting of my project, and the exception is still thrown.
Update 2
I can ignore the error, and everything gets executed just fine, however it fails whenever I perform a step-by-step execution. Of course, I can still put a breakpoint after, and jump right over the faulty line, but it's still annoying and does not explain why there is this error there in the first place.
The exception is on setting the SQL string - which tells you that it's wrong. As per #RogerCigol's comment, you should NOT have the ; at the end of your SQL string.
Kudos to Roger for that.
If you want to access parameters, you MUST set the SQL string first, it will be parsed to identify the parameters. The parameters will not exist until the string is parsed, or you manually create them (which is pointless as they would be recreated on parsing the string).
You can also access the parameters as an ordered index, and I have always been able to use ? as an anonymous parameter with MySQL.
I know, I know - that question title is very much all over the place. However, I am not sure what could be an issue here that is causing what I am witnessing.
I have the following method in class Project that is being unit tested:
bool Project::DetermineID(std::string configFile, std::string& ID)
{
std::ifstream config;
config.open(configFile);
if (!config.is_open()) {
WARNING << "Failed to open the configuration file for processing ID at: " << configFile;
return false;
}
std::string line = "";
ID = "";
bool isConfigurationSection = false;
bool isConfiguration = false;
std::string tempID = "";
while (std::getline(config, line))
{
std::transform(line.begin(), line.end(), line.begin(), ::toupper); // transform the line to all capital letters
boost::trim(line);
if ((line.find("IDENTIFICATIONS") != std::string::npos) && (!isConfigurationSection)) {
// remove the "IDENTIFICATIONS" part from the current line we're working with
std::size_t idStartPos = line.find("IDENTIFICATIONS");
line = line.substr(idStartPos + strlen("IDENTIFICATIONS"), line.length() - idStartPos - strlen("IDENTIFICATIONS"));
boost::trim(line);
isConfigurationSection = true;
}
if ((line.find('{') != std::string::npos) && isConfigurationSection) {
std::size_t bracketPos = line.find('{');
// we are working within the ids configuration section
// determine if this is the first character of the line, or if there is an ID that precedes the {
if (bracketPos == 0) {
// is the first char
// remove the bracket and keep processing
line = line.substr(1, line.length() - 1);
boost::trim(line);
}
else {
// the text before { is a temp ID
tempID = line.substr(0, bracketPos - 1);
isConfiguration = true;
line = line.substr(bracketPos, line.length() - bracketPos);
boost::trim(line);
}
}
if ((line.find("PORT") != std::string::npos) && isConfiguration) {
std::size_t indexOfEqualSign = line.find('=');
if (indexOfEqualSign == std::string::npos) {
WARNING << "Unable to determine the port # assigned to " << tempID;
}
else {
std::string portString = "";
portString = line.substr(indexOfEqualSign + 1, line.length() - indexOfEqualSign - 1);
boost::trim(portString);
// confirm that the obtained port string is not an empty value
if (portString.empty()) {
WARNING << "Failed to obtain the \"Port\" value that is set to " << tempID;
}
else {
// attempt to convert the string to int
int workingPortNum = 0;
try {
workingPortNum = std::stoi(portString);
}
catch (...) {
WARNING << "Failed to convert the obtained \"Port\" value that is set to " << tempID;
}
if (workingPortNum != 0) {
// check if this port # is the same port # we are publishing data on
if (workingPortNum == this->port) {
ID = tempID;
break;
}
}
}
}
}
}
config.close();
if (ID.empty())
return false;
else
return true;
}
The goal of this method is to parse any text file for the ID portion, based on matching the port # that the application is publishing data to.
Format of the file is like this:
Idenntifications {
ID {
port = 1001
}
}
In a separate Visual Studio project that unit tests various methods, including this Project::DetermineID method.
#define STRINGIFY(x) #x
#define EXPAND(x) STRINGIFY(x)
TEST_CLASS(ProjectUnitTests) {
Project* parser;
std::string projectDirectory;
TEST_METHOD_INITIALIZE(ProjectUnitTestInitialization) {
projectDirectory = EXPAND(UNITTESTPRJ);
projectDirectory.erase(0, 1);
projectDirectory.erase(projectDirectory.size() - 2);
parser = Project::getClass(); // singleton method getter/initializer
}
// Other test methods are present and pass/fail accordingly
TEST_METHOD(DetermineID) {
std::string ID = "";
bool x = parser ->DetermineAdapterID(projectDirectory + "normal.cfg", ID);
Assert::IsTrue(x);
}
};
Now, when I run the tests, DetermineID fails and the stack trace states:
DetermineID
Source: Project Tests.cpp line 86
Duration: 2 sec
Message:
Assert failed
Stack Trace:
ProjectUnitTests::DetermineID() line 91
Now, in my test .cpp file, TEST_METHOD(DetermineID) { is present on line 86. But that method's } is located on line 91, as the stack trace indicates.
And, when debugging, the unit test passes, because the return of x in the TEST_METHOD is true.
Only when running the test individually or running all tests does that test method fail.
Some notes that may be relevant:
This is a single-threaded application with no tasks scheduled (no race condition to worry about supposedly)
There is another method in the Project class that also processes a file with an std::ifstream same as this method does
That method has its own test method that has been written and passes without any problems
The test method also access the "normal.cfg" file
Yes, this->port has an assigned value
Thus, my questions are:
Why does the stack trace reference the closing bracket for the test method instead of the single Assert within the method that is supposedly failing?
How to get the unit test to pass when it is ran? (Since it currently only plasses during debugging where I can confirm that x is true).
If the issue is a race condition where perhaps the other test method is accessing the "normal.cfg" file, why does the test method fail even when the method is individually ran?
Any support/assistance here is very much appreciated. Thank you!
I have a HaxeFlixel project, that is working OK in Debug mode for misc targets, including flash, neko and windows. But Targeting Windows in Release mode, I'm having an unexpected crash, and surprisingly it's happening inside a try-catch block. Here's the crashing function:
/**
* Will safely scan a parent node's children, search for a child by name, and return it's text.
* #param parent an Fast object that is parent of the `nodeNamed` node
* #param nodeName the node's name or a comma-separated path to the child (will scan recursively)
* #return node's text as String, or null if child is not there
*/
public static function getNodeText(parent:Fast, nodeName:String):String {
try {
var _node : Fast = getNodeNamed(parent, nodeName);
//if (_node == null)
// return null;
// next line will crash if _node is null
var it :Iterator<Xml> = _node.x.iterator();
if ( it == null || !it.hasNext() )
return null;
var v = it.next();
var n = it.next();
if( n != null ) {
if( v.nodeType == Xml.PCData && n.nodeType == Xml.CData && StringTools.trim(v.nodeValue) == "" ) {
var n2 = it.next();
if( n2 == null || (n2.nodeType == Xml.PCData && StringTools.trim(n2.nodeValue) == "" && it.next() == null) )
return n.nodeValue;
}
//does not only have data (has children)
return null;
}
if( v.nodeType != Xml.PCData && v.nodeType != Xml.CData )
//does not have data";
return null;
return v.nodeValue;
}catch (err:Dynamic) {
trace("Failed parsing node Text [" + nodeName+"] " + err );
return null;
}
}
By enabling if (_node == null) return null; line, It's working safely again. By catching errors as Dynamic I thought I was supposed to catch every possible error type! Why is this happening? And why is it appearing in release mode?
My IDE is FlashDevelop, and I'm using HaxeFlixel 3.3.6, lime 0.9.7 and openFL 1.4.0, if that makes any difference
EDIT: I suspect this has to do with how the translated C++ code missed the Dynamic Exception. The equivalent generated C++ code is:
STATIC_HX_DEFINE_DYNAMIC_FUNC2(BaxXML_obj,_getNodeNamed,return )
::String BaxXML_obj::getNodeText( ::haxe::xml::Fast parent,::String nodeName){
HX_STACK_FRAME("bax.utils.BaxXML","getNodeText",0x4a152f07,"bax.utils.BaxXML.getNodeText","bax/utils/BaxXML.hx",56,0xf6e2d3cc)
HX_STACK_ARG(parent,"parent")
HX_STACK_ARG(nodeName,"nodeName")
HX_STACK_LINE(56)
try
{
HX_STACK_CATCHABLE(Dynamic, 0);
{
HX_STACK_LINE(57)
::haxe::xml::Fast _node = ::bax::utils::BaxXML_obj::getNodeNamed(parent,nodeName); HX_STACK_VAR(_node,"_node");
HX_STACK_LINE(63)
Dynamic it = _node->x->iterator(); HX_STACK_VAR(it,"it");
// ... Let's skip the irrelevant code
}
catch(Dynamic __e){
{
HX_STACK_BEGIN_CATCH
Dynamic err = __e;{
HX_STACK_LINE(82)
::String _g5 = ::Std_obj::string(err); HX_STACK_VAR(_g5,"_g5");
HX_STACK_LINE(82)
::String _g6 = (((HX_CSTRING("Failed parsing node Text [") + nodeName) + HX_CSTRING("] ")) + _g5); HX_STACK_VAR(_g6,"_g6");
HX_STACK_LINE(82)
::haxe::Log_obj::trace(_g6,hx::SourceInfo(HX_CSTRING("BaxXML.hx"),82,HX_CSTRING("bax.utils.BaxXML"),HX_CSTRING("getNodeText")));
HX_STACK_LINE(83)
return null();
}
}
}
HX_STACK_LINE(56)
return null();
}
What haxedefs do you have defined?
Adding these to your project.xml might help:
<haxedef name="HXCPP_CHECK_POINTER"/> <!--makes null references cause errors-->
<haxedef name="HXCPP_STACK_LINE" /> <!--if you want line numbers-->
<haxedef name="HXCPP_STACK_TRACE"/> <!--if you want stack traces-->
You might also try the crashdumper library:
https://github.com/larsiusprime/crashdumper
(Crashdumper will turn on HXCPP_CHECK_POINTER by default as part of it's include.xml, and will set up hooks for both hxcpp's errors and openfl/lime's uncaught error events)
I guess this boils down to how C++ handles null-pointer Exceptions. It doesn't!
More info here or here
That seems odd, some questions that may help solving it.
It looks like you are doing quite some assumptions on how the xml looks (doing some manual it.next()), why is that?
Why are you using this big-ass try-catch block?
How does getNodeNamed look, it seems it can return null.
Do you have an example xml to test with?
I'm sorry, it would be extremely difficult to make a fully reproducible version of the error --- so please bare with my schematic code.
This program retrieves information from a web page, processes it, and saves output to an ASCII file. I also have a 'log' file (FILE *theLog---contained within a Manager object) for reporting errors, etc.
Some background methods:
// Prints string to log file
void Manager::logEntry(const string lstr) {
if( theLog != NULL ) { fprintf(theLog, "%s", lstr.c_str()); }
}
// Checks if file with given name already exists
bool fileExists(const string fname) {
FILE *temp;
if( temp = fopen(fname.c_str(), "r") ) {
fclose(temp);
return true;
} else { return false; }
}
// Initialize file for writing (some components omitted)...
bool initFile(FILE *&oFile, const string fname) {
if(oFile = fopen(fname.c_str(), "w") ) { return true; }
else { return false; }
}
The stuff causing trouble:
// Gets data from URL, saves to file 'dataFileName', input control flag 'foreCon'
// stu is some object that has string which i want
bool saveData(Manager *man, Stuff *stu, string dataFileName, const int foreCon) {
char logStr[CHARLIMIT_LARGE]; // CHARLIMIT_LARGE = 2048
sprintf(logStr, "Saving Data...\n");
man->logEntry( string(logStr) ); // This appears fine in 'theLog' correctly
string data = stu->getDataPrefixStr() + getDataFromURL() + "\n"; // fills 'data' with stuff
data += stu->getDataSuffixStr();
if( fileExists(dataFileName) ) {
sprintf(logStr, "save file '%s' already exists.", dataFileName.c_str() );
man->logEntry( string(logStr) );
if( foreCon == -1 ) {
sprintf(logStr, "foreCon = %d, ... exiting.", foreCon); // LINE 'A' : THIS LINE ENDS UP IN OUTPUT FILE
tCase->logEntry( string(logStr) );
return false;
} else {
sprintf(logStr, "foreCon = %d, overwriting file.", foreCon); // LINE 'B' : THIS LINE ENDS UP IN LOG FILE
tCase->logEntry( string(logStr) );
}
}
// Initialize output file
FILE *outFile;
if( !initFile(outFile, dataFileName) ) {
sprintf(logStr, "couldn't initFile '%s'", dataFileName.c_str());
tCase->logEntry( string(logStr) );
return false;
}
fprintf(outFile, "%s", data.c_str()); // print data to output file
if( fclose(outFile) != EOF) {
sprintf(logStr, "saved to '%s'", dataFileName.c_str());
tCase->logEntry( string(logStr) );
return true;
}
return false;
}
If the file already exists, AND 'int foreCon = -1' then the code should print out line 'A' to the logFile. If the file exists and foreCon != -1, the old file is overwritten with data. If the file doesn't exist, it is created, and the data is written to it.
The result however, is that a broken up version of line 'A' appears in the data file AND line 'B' is printed in the log file!!!!
What the data file looks like:
.. exiting.20130127 161456
20130127 000000,55,17,11,0.00
20130127 010000,54,17,11,0.00
... ...
The second line and onward look correct, but there is an extra line that contains part of line 'A'.
Now, the REALLY WEIRD PART. If I comment out everything in the if( foreCon == -1) { ... } block, then the data file looks like:
%d, ... exiting.20130127 161456
20130127 000000,55,17,11,0.00
20130127 010000,54,17,11,0.00
... ...
There is still an extra line, but it is the LITERAL CODE copied into the data file.
I think there is a poltergeist in my code. I don't understand how any of this could happen.
Edit: I've tried printing to console the data string, and it gives the same messed up values: i.e. %d, ... exiting.20130127 161456 - so it must be something about the string instead of the FILE *
Answer based on your latest comment:
getDataPrefixStr() ends up returning a string which starts with
something like string retStr = COMCHAR + " file created on ..."; such
that const char COMCHAR = '#';. Could the COMCHAR be the problem??
You can't add characters and string literals (which are arrays of char, not strings) like that.
You're adding 35 (the ASCII for "#") to the address of " file created on ... ", i.e. getDataPrefixStr() is whatever starts 35 characters from the start of that string. Since all literal strings are stored together in the same data area, you'll get strings from the program in the output.
Instead, you cold do
const string COMCHAR = "*";
string retStr = COMCHAR + " file created on ...";
It could be that logStr is too short and that it is causing data to be overwritten in other buffers (did you double check CHARLIMIT_LARGE?). You can diagnose this by commenting all writes to logStr (sprintf) and see if data is still corrupted. In general, your code is vulnerable to this if a user can set dataFileName (to be a very long string); use snprintf or ostringstream instead.
Otherwise, I would guess that either stu->getDataPrefixStr() or getDataFromURL() are returning corrupted results or return type char* instead of string. Try printing these values to the console directly to see if they are corrupted or not. If they return a char*, then data = stu->getDataPrefixStr() + getDataFromURL() will have undefined behavior.
if( temp = fopen(fname.c_str(), 'r') ) {
should be
if( temp = fopen(fname.c_str(), "r") ) {