Currently writing an IRC bot for fun and I have some trouble setting up the bot to listen to my commands. ( Works for !quit but not for !join or !leave )
void onPrivMsg(IRCMessage message, IRCClient* client)
{
// received text
std::string text = message.parameters.at(message.parameters.size() - 1);
if (text[0] == '!')
{
if (text == "!Join #channel" || text == "!join #channel")
client->SendIRC("JOIN #channel");
if (text == "!Leave #channel" || text == "!leave #channel")
client->SendIRC("PART #channel");
if (text == "!Quit" || text == "!quit")
client->SendIRC("QUIT");
} else{
client->SendIRC("PRIVMSG #channel :Wrong command.");
}
}
I'm calling it like so:
client.HookIRCCommand("PRIVMSG", &onPrivMsg);
How would I get the channel name (#ChannelISpecify) from the text message line?
Example: If I'd type "!join #funnyposts" in IRC it would join the channel #funnyposts.
Appriciate any kind of help.
Figured it out. Now filters for different commands and acts accordingly.
if (text[0] == '!')
{
std::string commandApi = text.substr(0, text.find(" "));
if (commandApi == "!Join" || commandApi == "!join"){
if (commandApi[0] = '!'){
commandApi[0] = '/';
}
std::string channel2 = text.substr(text.find("#"));
client->SendIRC("PRIVMSG " + channel2 + " :Joining channel");
client->SendIRC("JOIN " + channel2);
}
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 need to redirect based on the country location the user is trying to access. For example when user trying to access http://www.example.com/ from china my site should as http://www.example.com/zh. I am checking using the sitecore tracker in pipeline process to get the country code using the below method.
public void Process(HttpRequestArgs args)
{
Assert.ArgumentNotNull(args, "args");
if (HttpContext.Current == null
|| Context.Site == null
////TODO: || Sitecore.Context.PageMode...
|| Context.Database == null || Context.Site.Name == "shell" || !this._sites.Contains(Context.Site.Name))
{
return;
}
// contains path including language and query string
// (not anchor name), but not hostname.
// We can use this to add the language back into the path.
string rawPath = Sitecore.Context.RawUrl;
if (!rawPath.StartsWith("/sitecore") && !rawPath.StartsWith("/" + Sitecore.Context.Language.Name + "/") && !rawPath.StartsWith("/" + Sitecore.Context.Language.Name) && !rawPath.StartsWith("/default.aspx"))
{
string langCode = "";
if(!string.IsNullOrEmpty(GeoIPUtils.GetUserGeoIP()))
{
try
{
string country = GeoIPUtils.GetUserGeoIP();;
if (country.Trim().ToUpper() == "China".ToUpper())
langCode = "zh";
else if (country.Trim().ToUpper() == "Japan".ToUpper())
langCode = "jp";
else if (country.Trim().ToUpper() == "Thailand".ToUpper())
langCode = "th";
else
langCode = "en";
}
catch(Exception)
{
langCode = "en";
}
}
else
{
langCode = HttpContext.Current.Request.Cookies["avc#lang"].Value.ToString();
}
if (!string.IsNullOrEmpty(langCode))
{
Language language = null;
if (Language.TryParse(langCode, out language))
{
//then try to get the language item id from the language or two letter iso code
ID langID = LanguageManager.GetLanguageItemId(language, Sitecore.Context.Database);
if (!ID.IsNullOrEmpty(langID))
{
//sometimes the language found is slightly different than official language item used in SC
language = LanguageManager.GetLanguage(language.CultureInfo.TwoLetterISOLanguageName);
if (Context.Item != null)
{
List<string> availableLangs = LanguagesWithContent(Context.Item);
if (availableLangs != null && availableLangs.Count > 0 && !availableLangs.Contains(language.CultureInfo.TwoLetterISOLanguageName.ToString()))
{
langCode = availableLangs.FirstOrDefault().ToString();
}
}
else
{
langCode = "en";
}
}
else
{
langCode = "en";
}
}
}
HttpContext.Current.Response.RedirectPermanent("/" + (String.IsNullOrEmpty(langCode) ? Sitecore.Context.Language.Name : langCode) + rawPath);
}
}
Below is the GetUserGeoIP function
public static string GetUserGeoIP()
{
string countryCode = "";
try
{
countryCode = Sitecore.Analytics.Tracker.Current.Interaction.GeoData.Country;
}
catch(Exception ex)
{
Log.Error("GetUserGeoIP Error: " + ex.Message + " Source: " + ex.Source + " Stack Trace :" + ex.StackTrace + " Inner Ex : " + ex.InnerException, ex);
countryCode = "GB";
}
if (!string.IsNullOrEmpty(countryCode))
{
var countryItem = ISO3166.FromAlpha2(countryCode);
if (countryItem != null)
return countryItem.Name;
}
return "Other";
}
But I am getting an below exception
7904 10:43:25 ERROR Cannot create tracker.
Exception: System.InvalidOperationException
Message: session is not initialized
Source: Sitecore.Analytics
at Sitecore.Analytics.Data.HttpSessionContextManager.GetSession()
at Sitecore.Analytics.Pipelines.EnsureSessionContext.EnsureContext.Process(InitializeTrackerArgs args)
at (Object , Object[] )
at Sitecore.Pipelines.CorePipeline.Run(PipelineArgs args)
at Sitecore.Analytics.DefaultTracker.EnsureSessionContext()
at Sitecore.Analytics.Pipelines.CreateTracker.GetTracker.Process(CreateTrackerArgs args)
at (Object , Object[] )
at Sitecore.Pipelines.CorePipeline.Run(PipelineArgs args)
at Sitecore.Analytics.Tracker.Initialize()
Note: The same GetUserGeoIP method is used in API which gets the correct countryName. I am using Sitecore.NET 8.0 (rev. 151127) version
Any help on this highly appreciated
Your processor is probably too soon in the pipeline(s). You can find an overview of the request pipelines here: http://sitecoreskills.blogspot.be/2015/02/a-sitecore-8-request-from-beginning-to.html
You should put your processor after the tracker has been initialized and the geo data has been fetched. I did something similar a while ago and placed my processor in the startAnalytics pipeline.
Fetching the geo data is async so that might (will) give issues when doing this in the first request pipeline. Read this article to tackle that issue by calling UpdateGeoIpData with a timespan.
I have made a simple Script reading class in C++ which allows me to read and parse scripts.
Basically there's a FILE class, which then I proceed to open with "fopen".
In functions I proceed to call "fgetc" and "ftell" to parse the script file as needed, note this ain't an interpreter.
Every script file is supposed to follow a syntax, but this is why I'm asking here for a solution.
Here's how a script looks like:
# Script File Comment
USERNAME = "Joe"
PASSWORD = "pw0001"
ACCESSLEVEL = 3
DATABASE = ("localhost",3306,"db","user","password")
Basically I have a few functions:
// This function searches for "variables"
nextToken();
// After I have the variable, e.g: USERNAME, PASSWORD, ACCESSLEVEL or DATABASE
// I proceed to call this function
// This function reads the char array for (,-{}()[]=) these are symbols
readSymbol();
// In a condition I check what "token/variable" I got and proceed to read
// it accordingly
// e.g; for USERNAME I do:
readString(); // reads text inside "
// e.g; for ACCESSLEVEL I do:
readNumber(); // reads digits until the next char ain't a digit
// e.g; for DATABASE I do:
readSymbol(); // (
readString(); // 127.0.0.1
readSymbol(); // ,
readNumber(); // 3306
readSymbol(); // ,
readString(); // db
readSymbol(); // ,
readString(); // user
readSymbol(); // ,
readString(); // password
readSymbol(); // )
I would like to be able to read a variable declaration like this:
DATABASELIST = {"data1","data2","data3"}
or
DATABASELIST = {"data1"}
I could easily do readSymbol and readString to read for 3 different string definitions inside the variable, however this list is supposed to have custom user data, like 5 different strings, or 8 different strings - depends.
And I seriously have no idea how can I do this with the parser I wrote.
Please note that I am basing this in some Pseudo code I took from a scripter for this type of format, I have the pseudo code extracted from IDA, if you would like to see it for better understanding post here
Here's an example of my "readSymbol" function.
READSYMBOL
int TReadScriptFile::readSymbol()
{
int currentData = 0;
int stringStart = -1;
// Check if we can't read anymore
if (end)
return 0;
while (true)
{
// Basically get chars in the script
currentData = fgetc(File);
// Check for end of file
if (currentData == -1)
{
end = true;
break;
}
if (stringStart == -1)
{
if (isdigit(currentData) || isalpha(currentData))
{
printf("TReadScriptFile::readSymbol: Symbol expected\n");
close();
return 0;
}
else if
(
currentData == '=' || currentData == ',' ||
currentData == '(' || currentData == ')' ||
currentData == '{' || currentData == '}' ||
currentData == '>' || currentData == '<' ||
currentData == ':' || currentData == '-'
)
{
#ifdef __DEBUG__
printf("Symbol: %c\n", currentData);
#endif
stringStart = ftell(File);
break;
}
}
}
return 1;
}
NEXTTOKEN
int TReadScriptFile::nextToken()
{
int currentData = 0;
int stringStart = -1;
int stringEnd = -1;
RecursionDepth = -1;
memset(String, 0, 4000);
// Check if we can't read anymore
if (end)
return 0;
while (true)
{
// ** Syntax **
if (isdigit(getNext()) || getNext() == -1)
{
printf("No more tokens left.\n");
end = true;
close();
return 0;
}
// End
// Basically get chars in the script
currentData = fgetc(File);
// Check for end of file
if (currentData == -1)
{
end = true;
break;
}
// Syntax Checking Part, this really isn't needed but w/e
if (stringStart == -1)
{
if (currentData == '=' || isdigit(currentData))
{
printf("TReadScriptFile::nextToken: Syntax Error: string expected\n");
close();
return 0;
}
}
// End Syntax Checking
// It's a comment line, we should skip
if (currentData == '#')
{
seekNewLn();
continue;
}
// There are no variables, yet
if (stringStart == -1)
{
// We found a letter, we are near a token!
if (isalpha(currentData))
{
stringStart = ftell(File);
// We might as well add the letter to the string
RecursionDepth++;
String[RecursionDepth] = currentData;
continue;
}
}
else if (stringStart != -1)
{
// Let's wait until we get an identifier or space
// We found a digit, error
if (isdigit(currentData))
{
printf("TReadScriptFile::nextToken: string expected\n");
close();
return 0;
}
// We found a space, maybe we should stop looking for tokens?
else if (isspace(currentData))
{
#ifdef __DEBUG__
printf("Token: %s\n", String);
#endif
break;
}
RecursionDepth++;
String[RecursionDepth] = currentData;
}
}
return 1;
}
I found a good example of the approach I followed here:
http://llvm.org/docs/tutorial/LangImpl1.html
One mechanism to deal with DATABASE_LIST would be this:
After finding the variable DATABASE_LIST read a symbol using readSymbol() checking if it is a { then in a loop do readString() add it to a std::vector (or some other suitable container) then check for a , or } (using readSymbol()) . If it is a ,(comma) then you go back and read another string add to the vector etc. until you do finally reach } . When you are finished you'd have a vector (dynamic array) of strings that represent a DATABASE_LIST
In this cryptography post it says
The chain can go as long as you want, until it hits the original input. When it hits that point, it will just repeat itself and it will be useless.
So my starting point is 12345 but I can't get the end point and having an endless loop because 12345 doesn't repeat. I'm using qt4.7 (lib version: 4.7.3) to achieve this. Here is my code
rainbowTable::rainbowTable(QWidget *parent) :
QWidget(parent),
ui(new Ui::rainbowTable)
{
ui->setupUi(this);
passwordLength = 5;
qDebug() << getLastReduction("12345",false);
}
QString rainbowTable::hashString(QString value)
{
QString dataToReturn = QString(QCryptographicHash::hash((value.toAscii()),QCryptographicHash::Md5).toHex());
return dataToReturn;
}
QString rainbowTable::reductionOfString(QString hash)
{
QString dataToReturn = "";
int iterator = 0;
while ( iterator < hash.count() )
{
if ( hash.at(iterator) == '0' ||
hash.at(iterator) == '1' ||
hash.at(iterator) == '2' ||
hash.at(iterator) == '3' ||
hash.at(iterator) == '4' ||
hash.at(iterator) == '5' ||
hash.at(iterator) == '6' ||
hash.at(iterator) == '7' ||
hash.at(iterator) == '8' ||
hash.at(iterator) == '9' )
{
dataToReturn += hash.at(iterator);
if( dataToReturn.count() == passwordLength )
break;
}
iterator++;
}
return dataToReturn;
}
QString rainbowTable::getLastReduction(QString value,bool isHash)
{
int flagToAvoidImmediateExit = 0;
if( isHash )
{
QString startPoint = value;
startPoint = reductionOfString(startPoint);
QString endPoint = "";
QString tempPoint = startPoint;
while( startPoint != tempPoint || flagToAvoidImmediateExit == 0 )
{
flagToAvoidImmediateExit = 1;
endPoint = tempPoint;
tempPoint = hashString(tempPoint);
tempPoint = reductionOfString(tempPoint);
qDebug() << tempPoint;
}
return endPoint;
}
else
{
QString startPoint = value;
QString endPoint = "";
QString tempPoint = startPoint;
while( startPoint != tempPoint || flagToAvoidImmediateExit == 0 )
{
flagToAvoidImmediateExit = 1;
endPoint = tempPoint;
tempPoint = hashString(tempPoint);
tempPoint = reductionOfString(tempPoint);
qDebug() << tempPoint;
}
return endPoint;
}
}
Here is the debug ouput for a few seconds:
"38064"
"37923"
"59636"
"14842"
"81105"
"83011"
"84978"
"72903"
"28301"
"59067"
"94222"
"35329"
"75907"
"52980"
"64297"
"36654"
"12207"
"83738"
"03523"
"79083"
"15597"
"32652"
"13934"
"88497"
"75435"
"79791"
"58265"
"09856"
"18041"
"43966"
"65978"
"64242"
"52739"
"55704"
"56811"
"58183"
"68597"
"84064"
"85717"
"46438"
"18042"
"71321"
"88067"
"70648"
"83580"
"11878"
"32297"
"52376"
"41289"
"07909"
"50439"
"03819"
"50325"
"82736"
"41621"
"05497"
"15546"
"64017"
"90503"
"13150"
"30287"
"01749"
"81308"
"12036"
"37241"
"35850"
"97225"
"80539"
"17472"
"63098"
"85818"
"18438"
"26139"
"09545"
"97042"
"63672"
"37406"
"41180"
"14910"
"28900"
"29729"
"56861"
"16208"
"83565"
"30912"
"95541"
"08468"
"29539"
"93679"
"42487"
"95833"
"42793"
"97064"
"18087"
"75623"
"13910"
"60404"
"52557"
"95932"
"65477"
"28304"
"08456"
"27849"
"11429"
"38896"
"08634"
"97107"
"96385"
"44159"
"32875"
"17063"
"86213"
"85052"
"46852"
"97541"
"81412"
"31199"
"96618"
"16178"
"56100"
"50394"
"42087"
"90552"
"51966"
"13598"
"28757"
"38715"
"71025"
"61334"
"43686"
"74633"
"50360"
"99883"
"01361"
"49662"
"62929"
"07280"
"59161"
"32509"
"93670"
"95649"
"15206"
"99927"
"93692"
"37748"
"23350"
"74680"
"68259"
"04819"
"26627"
"65968"
"06919"
"09194"
"50084"
"74452"
"23763"
"17953"
"35026"
"86691"
"67542"
"95634"
"00793"
"20270"
"24386"
"35606"
"76055"
"00010"
"00798"
"30867"
"20697"
"02143"
"12044"
"05098"
"52828"
"98446"
"54039"
"08778"
"98405"
"92267"
"71783"
"61953"
"87447"
"66505"
"66535"
"01776"
"90120"
"51497"
"56082"
"18253"
"15222"
"74769"
"19614"
"86376"
"65391"
"43365"
"90484"
"32717"
"75052"
"16186"
"89444"
"15439"
"65166"
"75785"
"72462"
"75920"
"91383"
"41678"
"94123"
"61751"
"47976"
"67798"
"59438"
"10180"
"65854"
"40218"
"77990"
"44843"
"84554"
"52350"
"73347"
"51901"
"61155"
"30316"
"83096"
"64946"
"05985"
"24208"
"28718"
"02241"
"22303"
"23331"
"18410"
"54868"
"51723"
"06401"
"49554"
"65577"
"28105"
"42319"
"34167"
"85036"
"98679"
"08594"
"31075"
"80514"
"11517"
"66780"
"33411"
"83180"
"61910"
"70423"
"16885"
"09107"
"83702"
"81842"
"88430"
"59146"
"29140"
"47236"
"29625"
"03078"
"26540"
"79321"
"41649"
"10210"
"75702"
"12020"
"36877"
"57307"
"03222"
"46603"
"58449"
"94709"
"01436"
"84975"
"39385"
"15952"
"67607"
"91666"
"34456"
"53385"
"21512"
"06712"
"42073"
"61343"
"66825"
"70199"
"73203"
"60216"
"39469"
"84324"
"47850"
"84825"
"52471"
"92397"
"86051"
"33676"
"04221"
"79740"
"11573"
"26304"
"52510"
"12679"
"05930"
"49607"
"10880"
"99174"
"53967"
"06397"
"25700"
"96721"
"94694"
"96566"
"31746"
"57359"
"84870"
"06236"
"10673"
"45914"
"19209"
"32478"
"38824"
"71178"
"22983"
"36320"
"46594"
"66538"
"80495"
"35645"
"38064"
"37923"
"59636"
"14842"
"81105"
"83011"
"84978"
"72903"
"28301"
"59067"
"94222"
"35329"
"75907"
"52980"
"64297"
"36654"
"12207"
"83738"
"03523"
"79083"
"15597"
"32652"
"13934"
"88497"
"75435"
"79791"
"58265"
"09856"
"18041"
"43966"
"65978"
"64242"
"52739"
"55704"
"56811"
"58183"
"68597"
"84064"
"85717"
"46438"
"18042"
"71321"
"88067"
"70648"
"83580"
"11878"
"32297"
"52376"
"41289"
"07909"
"50439"
"03819"
"50325"
"82736"
"41621"
"05497"
"15546"
"64017"
"90503"
"13150"
"30287"
"01749"
"81308"
"12036"
"37241"
"35850"
"97225"
"80539"
"17472"
"63098"
"85818"
"18438"
"26139"
"09545"
"97042"
"63672"
"37406"
"41180"
"14910"
"28900"
"29729"
"56861"
"16208"
"83565"
"30912"
"95541"
"08468"
"29539"
"93679"
"42487"
"95833"
"42793"
"97064"
"18087"
"75623"
"13910"
"60404"
"52557"
"95932"
"65477"
"28304"
"08456"
"27849"
"11429"
"38896"
"08634"
"97107"
"96385"
"44159"
"32875"
"17063"
"86213"
"85052"
"46852"
"97541"
"81412"
"31199"
"96618"
"16178"
"56100"
"50394"
"42087"
"90552"
"51966"
"13598"
"28757"
"38715"
"71025"
"61334"
"43686"
"74633"
"50360"
"99883"
"01361"
"49662"
"62929"
"07280"
"59161"
"32509"
"93670"
"95649"
"15206"
"99927"
"93692"
"37748"
"23350"
"74680"
"68259"
"04819"
"26627"
"65968"
"06919"
"09194"
"50084"
"74452"
"23763"
"17953"
"35026"
"86691"
"67542"
"95634"
"00793"
"20270"
"24386"
"35606"
"76055"
"00010"
"00798"
"30867"
"20697"
"02143"
"12044"
"05098"
"52828"
"98446"
"54039"
"08778"
"98405"
"92267"
"71783"
"61953"
"87447"
"66505"
"66535"
"01776"
"90120"
"51497"
"56082"
"18253"
"15222"
"74769"
"19614"
"86376"
"65391"
"43365"
"90484"
As you see 12345 doesn't repeat but other numbers are repeated and having endless loop. Is my starting point wrong?
The chain is not guaranteed to ever hit the initial value again. More often than not you'll probably find it entering a loop like this:
If the input is larger than the hash output, it is impossible by definition to ever hit the initial input value again. However, even if the input has an equal length to the output, it is not guaranteed that the hash will cover every single possible value in the output space before looping around. This actually depends on the characteristics and quality of the hash. A hash may have one big cycle, covering every single possible output value in its loop. Other hashes may enter a number of different possible loops, each covering a different subset of the output space. Other hashes may not ever cover every possible output value.
To parse function parameters I get from JavaScript, I need to perform a lot of checks. For example a function might expect an object as parameter that looks like this in JavaScript.
{
Fullscreen: [ 'bool', false ],
Size: [ 'Vector2u', 800, 600 ],
Title: [ 'string', 'Hello World' ],
// more properties...
}
In C++ I parse this by looping over all keys and check them. If one of those checks fail, an error message should be printed and this key value pair should be skipped. This is how my implementation looks at the moment. I hope you doesn't get distracted from some of the engine specific calls.
ModuleSettings *module = (ModuleSettings*)HelperScript::Unwrap(args.Data());
if(args.Length() < 1 || !args[0]->IsObject())
return v8::Undefined();
v8::Handle<v8::Object> object = args[0]->ToObject();
auto stg = module->Global->Get<Settings>("settings");
v8::Handle<v8::Array> keys = object->GetPropertyNames();
for(unsigned int i = 0; i < keys->Length(); ++i)
{
string key = *v8::String::Utf8Value(keys->Get(i));
if(!object->Get(v8::String::New(key.c_str()))->IsArray())
{
HelperDebug::Fail("script", "could not parse (" + key + ") setting");
continue;
}
v8::Handle<v8::Array> values = v8::Handle<v8::Array>::Cast(object->Get(v8::String::New(key.c_str())));
if(!values->Has(0) || !values->Get(0)->IsString())
{
HelperDebug::Fail("script", "could not parse (" + key + ") setting");
continue;
}
string type = *v8::String::Utf8Value(values->Get(0));
if(type == "bool")
{
if(!values->Has(1) || !values->Get(1)->IsBoolean())
{
HelperDebug::Fail("script", "could not parse (" + key + ") setting");
continue;
}
stg->Set<bool>(key, values->Get(1)->BooleanValue());
}
else if(type == "Vector2u")
{
if(!values->Has(1) || !values->Has(2) || !values->Get(1)->IsUint32(), !values->Get(2)->IsUint32())
{
HelperDebug::Fail("script", "could not parse (" + key + ") setting");
continue;
}
stg->Set<Vector2u>(key, Vector2u(values->Get(1)->Uint32Value(), values->Get(2)->Uint32Value()));
}
else if(type == "string")
{
if(!values->Has(1) || !values->Get(1)->IsString())
{
HelperDebug::Fail("script", "could not parse (" + key + ") setting");
continue;
}
stg->Set<string>(key, *v8::String::Utf8Value(values->Get(1)));
}
}
As you can see, I defined when happens when a check fails at every filter.
HelperDebug::Fail("script", "could not parse (" + key + ") setting");
continue;
I would like to only write that once, but I can only come up with a way using goto which I would like to prevent. Is there a better option to restructure the if else construct?
I think I'd start with a set of small classes to do the verification step for each type:
auto v_string = [](v8::Handle<v8::Array> const &v) {
return v->Has(1) && v->Get(1)->IsString();
}
auto v_Vector2u = [](v8::Handle<v8::Array> const &v) {
return v->Has(1) && v->Has(2) &&
v->Get(1)->IsUint32() && v->Get(2)->IsUint32();
}
// ...
Then I'd create a map from the name of a type to the verifier for that type:
std::map<std::string, decltyp(v_string)> verifiers;
verifiers["string"] = v_string;
verifiers["Vector2u"] = v_Vector2u;
// ...
Then to verify a type, you'd use something like this:
// find the verifier for this type:
auto verifier = verifiers.find(type);
// If we can't find a verifier, reject the data:
if (verifier == verifiers.end())
HelperDebug::Fail("script", "unknown type: " + type);
// found the verifier -- verify the data:
if (!verifier->second(values))
HelperDebug::Fail("script", "could not parse (" + key + ") setting");
A pretty usual way for such situations is to use a macro. That is #defined before or in the funciton and #undef-ed at function end. As you need continue there, the other ways are pretty much hosed.
goto would also be a solution but for this particular sample I'd not go with it.
A lighter way is to put at least the fail call into a function or lambda, what still leaves you with the continue part.
I'd probably make a macro that takes the filter expression as argument, leaving code like.
if(type == "bool")
{
ENSURE(values->Has(1) && values->Get(1)->IsBoolean());
stg->Set<bool>(key, values->Get(1)->BooleanValue());
}
...