Refactoring switch or if/else statement? [closed] - c++

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
This question does not appear to be about programming within the scope defined in the help center.
Closed 5 years ago.
Improve this question
i'm working on a school project and got some feedback from my teacher. He said that in my code there are some bad practices, he said that the switch cases could be replaced by a polymorphic approach. Only i have no clue how i could do this.
My code is receiving messages from a CAN bus. Those messages come from different devices, I check the messages from which device they come from. If there is a new device I create a object and parse the message and store the information.
This system is pretty much the same for each message.
Here is my code.
void Application::PollWhisperConnectBus()
{
HAL_GPIO_TogglePin(PORT_LED1, PIN_LED1);
whisper_connect_id_ = hcan2.pRxMsg->StdId;
if (whisper_connect_id_ >= 0x580 && whisper_connect_id_ <= 0x58F)
{
WIBDevice();
}
if (whisper_connect_id_ >= 0x590 && whisper_connect_id_ <= 0x59F)
{
BMSSDevice();
}
if (whisper_connect_id_ >= 0x5B0 && whisper_connect_id_ <= 0x5BF)
{
DCPowerCubeDevice();
}
if (whisper_connect_id_ >= 0x5C0 && whisper_connect_id_ <= 0x5CF)
{
ACPowerCubeDevice();
}
if (whisper_connect_id_ >= 0x700 && whisper_connect_id_ <= 0x70F)
{
WIBHeartBeatDevice();
}
}
This is one of the functions which checked if there is an object of the class, if so parse the message.
void Application::DCPowerCubeDevice()
{
bool found_device = false;
int device = (hcan2.pRxMsg->StdId & 0x0F) + device_instance_offset_;
WhisperConnectDevice* whisper_connect_device;
for(unsigned int i = 0; i < whisper_connect_device_list_.size(); ++i)
{
if ((whisper_connect_device = whisper_connect_device_list_.at(i)) != NULL &&
whisper_connect_device->GetClassName() == "DCPowerCube")
{
DCPowerCube* dc_powercube = dynamic_cast<DCPowerCube*>(whisper_connect_device);
if (dc_powercube != NULL)
{
if (dc_powercube->GetDevice() == device)
{
dc_powercube->ParseCanMessage(&hcan2);
found_device = true;
break;
}
}
}
}
if (!found_device)
{
WhisperConnectDevice* dc_powercube;
if ((dc_powercube = new DCPowerCube) != NULL)
{
dc_powercube->SetDevice(device);
int n2k_address = nmea2000_.FindFirstFreeCanId(n2k_address_, device_list_);
if (n2k_address != 0xFFFF)
{
dc_powercube->SetSrcCanId(n2k_address);
dc_powercube->SetDeviceInstanceOffset(device_instance_offset_);
dc_powercube->SetDeviceInstance(0x30 + device);
dc_powercube->AddressClaim(nmea2000_);
dc_powercube->SendPGN126996(nmea2000_);
dc_powercube->SendPGN126998(nmea2000_, "DCPowerCube", "", "");
device_list_.at(n2k_address) = 0x01;
}
DCPowerCube* dc_powercube2 = dynamic_cast<DCPowerCube*>(dc_powercube);
if (dc_powercube2 != NULL)
{
dc_powercube2->SetCurrentLimit(16);
}
AddToWPCDeviceList(dc_powercube);
}
}
}
void DCPowerCube::ParseCanMessage(CAN_HandleTypeDef *can_handle)
{
if (can_handle != NULL)
{
uint16_t message_index = (can_handle->pRxMsg->Data[1] << 8) + can_handle->pRxMsg->Data[2];
switch (message_index)
{
case 0x1008:
device_name_[0] = can_handle->pRxMsg->Data[4];
device_name_[1] = can_handle->pRxMsg->Data[5];
device_name_[2] = can_handle->pRxMsg->Data[6];
device_name_[3] = can_handle->pRxMsg->Data[7];
device_name_[4] = '\0';
break;
case 0x100A:
software_version_[0] = can_handle->pRxMsg->Data[4];
software_version_[1] = can_handle->pRxMsg->Data[5];
software_version_[2] = can_handle->pRxMsg->Data[6];
software_version_[3] = can_handle->pRxMsg->Data[7];
software_version_[4] = '\0';
break;
case 0x1018:
serial_number_ = can_handle->pRxMsg->Data[4] << 24 | can_handle->pRxMsg->Data[5] << 16 |
can_handle->pRxMsg->Data[6] << 8 | can_handle->pRxMsg->Data[7];
break;
case 0x2100: // DC PowerCube status
power_cube_status_ = can_handle->pRxMsg->Data[4];
io_status_bit_ = can_handle->pRxMsg->Data[5];
dip_switch_status_bit_ = can_handle->pRxMsg->Data[6];
break;
case 0x2111: // Grid voltage, current, current limit
grid_voltage_ = (can_handle->pRxMsg->Data[4] << 8) + can_handle->pRxMsg->Data[5];
grid_current_ = can_handle->pRxMsg->Data[6];
grid_current_limit_ = can_handle->pRxMsg->Data[7];
break;
case 0x2112: // Generator frequency, RPM
generator_freq_ = (can_handle->pRxMsg->Data[4] << 8) + can_handle->pRxMsg->Data[5];
rpm_ = (can_handle->pRxMsg->Data[6] << 8) + can_handle->pRxMsg->Data[7];
break;
case 0x2113: // Generator current
gen_current_phase1_ = can_handle->pRxMsg->Data[4];
gen_current_phase2_ = can_handle->pRxMsg->Data[5];
gen_current_phase3_ = can_handle->pRxMsg->Data[6];
gen_current_limit_ = can_handle->pRxMsg->Data[7];
break;
case 0x2114: // Load percentage
grid_load_ = can_handle->pRxMsg->Data[4];
generator_load_ = can_handle->pRxMsg->Data[5];
dc_output_load_ = can_handle->pRxMsg->Data[6];
break;
case 0x2151: // Battery type & charger state
battery_type_ = can_handle->pRxMsg->Data[4];
charger_state_ = can_handle->pRxMsg->Data[5];
break;
case 0x2152: // DC output voltage & DC slave voltage
dc_output_voltage_ = (can_handle->pRxMsg->Data[4] << 8) + can_handle->pRxMsg->Data[5];
dc_slave_voltage_ = (can_handle->pRxMsg->Data[6] << 8) + can_handle->pRxMsg->Data[7];
break;
case 0x2153: // DC output current & DC output current limit
dc_output_current_ = (can_handle->pRxMsg->Data[4] << 8) + can_handle->pRxMsg->Data[5];
dc_output_current_limit_ = (can_handle->pRxMsg->Data[6] << 8) + can_handle->pRxMsg->Data[7];
break;
case 0x21A0: // Temperature sensor
temp_sens_BTS_ = can_handle->pRxMsg->Data[4];
temp_sens_intern1_ = can_handle->pRxMsg->Data[5];
temp_sens_intern2_ = can_handle->pRxMsg->Data[6];
temp_sens_intern3_ = can_handle->pRxMsg->Data[7];
break;
case 0x21A1:
break;
}
}
}
The WhisperConnectDevice is the base class of DCPowerCube.
I would love to get some feedback on how to approach this problem.

Whether or not you introduce polymorphism it appears you have to map an externally provided type number (ID) to code so you will always need some structure inbetween.
Your candidates are:
A block of if statements probably if-else-if...
A switch statement (if values are ameanable)
Some kind of look-up table (array, associative map, other...)
You've already got if but could improve with if-else-if.
That is normally considered the ugliest high-maintenance potential coding hot-spot approach. Coding hot-spot because all new IDs return to this code block.
I also notice in this case all your ranges are 0xnn0 to 0xnnF inclusive for some nn so you can at least simplify by reducing out the low 4 bits:
auto whisper_connect_type = whisper_connect_id_ >> 4;
Your switch option is then simplified to:
switch(whisper_connect_type) {
case 0x58: WIBDevice(); break;
case 0x59: BMSSDevice(); break;
case 0x5B: DCPowerCubeDevice(); break;
case 0x5C: ACPowerCubeDevice(); break;
case 0x70: WIBHeartBeatDevice(); break;
default: HandleUnknownDeviceIDError(whisper_connect_id_); break;
}
NB: I very strongly recommend some code to handle an unsupported ID. My advice is throwing an exception or something leading to termination. The break; is for completeness. I don't think you're coming back from an unknown ID.
An alternative is to define an associative map:
#include <iostream>
#include <unordered_map>
#include <memory>
class WhisperHandler {
public:
virtual void HandleWhisper() const = 0 ;
virtual ~WhisperHandler() {}
};
class WhisperHandlerWIBDevice : public WhisperHandler {
public:
void HandleWhisper() const override {
std::cout << "Handler WIBDevice...\n";
}
} ;
int main() {
std::unordered_map<unsigned,std::unique_ptr<const WhisperHandler>> handlers;
//...
std::unique_ptr<const WhisperHandler> handler(std::make_unique<const WhisperHandlerWIBDevice>());
std::pair<const unsigned , std::unique_ptr<const WhisperHandler> > pair({0x5B,std::move(handler)});
handlers.insert(std::move(pair));
//...
{
const auto &chandlers=handlers;
auto handlerit(chandlers.find(0x5B1));
if(handlerit!=chandlers.end()){
handlerit->second->HandleWhisper();
}else{
//ERROR - UNKNOWN HANDLER.
}
}
return 0;
}
I would suggest however you're only going to get return on investment for all this polymorphic machinery if you're going to allow the registration of handlers dynamically either from different modules of the application or by dynamically loading libraries that register themselves on load.
If it's a single project application (which it appears to be) then the switch table dispatch will probably work fine.
Because applications tend to communicate using IDs of some kind OO can start to look cumbersome when it in practice it needs to take an ID, map it to a polymorphic handler and then call the handler. Logically you've done the ID to logic mapping twice!
Footnote: The trick of knocking out the lowest 4-bits is somewhat separate from these methods and (of course) slightly fragile if the lower 4 bits become relevant to determining the handler down the line.

Related

Matlab crash after returning from function that calls MEX

I'm creating a MEX to find a path between two nodes using the A* algorithm. The code works as expected, retrieves the right result and everything seems to be fine, but when I return from the method that calls the MEX, Matlab simply shuts itself down.
As I know sometimes Matlab creates a pointer to the same memory address when the user tries to create a copy of a variable (I.E. A = 3; B = A, then A and B are pointing to the same memory address, even though Matlab shows them as 2 independent variables) I used an old trick that consists on performing an operation directly to the copy of the variable, even if its a silly one, Matlab will think that the two variables are no longer the same and will create a copy of it (I.E. A = 3; B = A; B = B+0, now A and B are stored as different and independent variables).
So the only way that I have found to fix the issue is to do what is shown below in function getWP:
function testAStarC()
% load the map of the area
load('epsp0_2_nav.mat');
% Set the initial node
initNode = '16x21';
% Set the target node
finalNode = '-15x54';
% Select Heuristic
heuristic = 'Manhattan';
% Create a target location (targetX, targetY, targetAngle)
targetCoords = [-15*110 54*110 0.15];
% Function that hosts the call to the MEX
wp = getWP(map, initNode, finalNode, targetCoords, heuristic);
disp('If you remove the line cellNodes{keyID}.x = cellNodes{keyID}.x; from getWP ...
I wont reach this line');
disp(['Route with ',num2str(length(wp)),' wp found']);
disp('done');
function waypointsList = getWP(map, initNode, finalNode, targetCoords, heuristic)
% HashMap containing the nodes (this is a Java hashmap)
nodesHash = map.navMap.nodes.nodes;
keys = nodesHash.keys();
numNodes = length(keys);
cellNodes = cell(1,numNodes);
% Parse the nodes from the HashMap to Cells as the MEX seems to be
% unable to read directly from the Java HashMap
for keyID=1:numNodes
cellNodes{keyID} = nodesHash(keys{keyID});
%---------------------------------------------------------
% WITHOUTH THIS MATLAB CRASHES WHEN RETURNING FROM GETWP
%---------------------------------------------------------
% We need this to force Matlab to create a new copy of the content,
% otherwise will send a pointer aiming to the HashMap and crash when
% returning from getWP.
cellNodes{keyID}.x = cellNodes{keyID}.x;
end
waypointsList = AStar(cellNodes, initNode, finalNode, targetCoords, heuristic, 1);
disp('I am not crashing here if you remove cellNodes{keyID}.x = cellNodes{keyID}.x');
My first thought was that I was doing something wrong to "cellNodes" inside the MEX and this was causing Matlab to crash, but I am not performing any operations using directly the input parameter. This is the constructor for the Node class:
Node.cpp
Node::Node(mxArray *cellElement)
{
double *xIn;
double *yIn;
char strIn[15];
double *posXIn;
double *posYIn;
double *weightIn;
double *tempVal;
size_t numCellElms;
mxArray *cellElement2;
numCellElms = mxGetNumberOfFields(cellElement);
size_t size;
for (int cellItem = 0; cellItem < numCellElms; cellItem++)
{
cellElement2 = mxGetCell(cellElement,cellItem);
if (cellItem == 0)
{
xIn = mxGetPr(cellElement2);
memcpy(tempVal,xIn,sizeof(double));
gridX = int(*tempVal);
}
if (cellItem == 1)
{
yIn = mxGetPr(cellElement2);
memcpy(tempVal,yIn,sizeof(double));
gridY = int(*tempVal);
}
if (cellItem >= 2 && cellItem < 10)
{
mwSize buflen = mxGetN(cellElement2)*sizeof(mxChar)+1;
if (buflen <= 1)
{
connections[cellItem-2][0] = '\0';
}
else
{
mxGetString(cellElement2, strIn, buflen);
memcpy(&connections[cellItem-2][0], strIn, buflen);
}
}
if (cellItem == 10)
{
posXIn = mxGetPr(cellElement2);
memcpy(&posX,posXIn,sizeof(double));
}
if (cellItem == 11)
{
posYIn = mxGetPr(cellElement2);
memcpy(&posY,posYIn,sizeof(double));
}
if (cellItem == 12)
{
posXIn = mxGetPr(cellElement2);
memcpy(&wpX,posXIn,sizeof(double));
}
if (cellItem == 13)
{
posYIn = mxGetPr(cellElement2);
memcpy(&wpY,posYIn,sizeof(double));
}
if (cellItem == 14)
{
weightIn = mxGetPr(cellElement2);
memcpy(&weight,weightIn,sizeof(double));
}
}
sprintf(xStr,"%i",gridX);
sprintf(yStr,"%i",gridY);
sprintf(key,"%ix%i",gridX,gridY);
}
And this is how I initialize the Nodes list inside the AStar.cpp
// Create nodes in the nodes hash
mxArray *cellElement;
std::map<std::string, Node> nodesHash;
for (int nodeID=0; nodeID < numberOfNodes; nodeID++)
{
cellElement = mxGetCell(prhs[0], nodeID);
Node n(cellElement);
nodesHash[n.key] = n;
}
Form now on nothing uses prhs[0] anymore, as nothing has altered the content of the prhs[0] (variable containing the pointer to the Matlab variable cellNodes), this variable should be exactly the same after leaving the MEX.
From here I have two questions:
If nothing is altering the content of the first parameter, why should it crash when returning from getWP?
Is there a more elegant way of forcing Matlab to create a real copy of a variable?
Thanks!
*Edit1: Using Matlab 2015b in Windows10 64 bit.
SOVED by #CrisLuengo and #JamesTursa.
1) If nothing is altering the content of the first parameter, why should it crash when returning from getWP?
[Node.cpp] 'double* tempVal' was not allocated, although the code worked, this was possibly messing with the integrity of prhs[0] thus causing Matlab to crash when returning from the function that invoked the MEX.
The solution was to declare tempVal as 'double tempVal[1]'.
After that, the testAStarC.m the line 'cellNodes{keyID}.x = cellNodes{keyID}.x;' can be removed without causing the error.
Although unrelated to the crash, the use of memcpy to get scalar doubles has been replaced with mxGetScalar().
Node constructor:
Node::Node(mxArray *cellElement)
{
char strIn[15];
double tempVal[1];
size_t numCellElms = mxGetNumberOfFields(cellElement);
mwSize buflen;
for (int cellItem = 0; cellItem < numCellElms; cellItem++)
{
mxArray *cellElement2 = mxGetCell(cellElement,cellItem);
switch (cellItem)
{
case 0:
gridX = (int)mxGetScalar(cellElement2);
break;
case 1:
gridY = (int)mxGetScalar(cellElement2);
break;
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
case 8:
case 9:
buflen = mxGetN(cellElement2)*sizeof(mxChar)+1;
if (buflen <= 1)
connections[cellItem-2][0] = '\0';
else
{
mxGetString(cellElement2, strIn, buflen);
memcpy(&connections[cellItem-2][0], strIn, buflen);
}
break;
case 10:
posX = mxGetScalar(cellElement2);
break;
case 11:
posY = mxGetScalar(cellElement2);
break;
case 12:
wpX = mxGetScalar(cellElement2);
break;
case 13:
wpY = mxGetScalar(cellElement2);
break;
case 14:
weight = mxGetScalar(cellElement2);
break;
}
}
sprintf(xStr,"%i",gridX);
sprintf(yStr,"%i",gridY);
sprintf(key,"%ix%i",gridX,gridY);
}
testAStarC.m
function waypointsList = getWP(map, initNode, finalNode, targetCoords, heuristic)
% HashMap containing the nodes (this is a Java hashmap)
nodesHash = map.navMap.nodes.nodes;
keys = nodesHash.keys();
numNodes = length(keys);
cellNodes = cell(1,numNodes);
% Parse the nodes from the HashMap to Cells as the MEX seems to be
% unable to read directly from the Java HashMap
for keyID=1:numNodes
cellNodes{keyID} = nodesHash(keys{keyID});
end
waypointsList = AStar(cellNodes, initNode, finalNode, targetCoords, heuristic, 1);
2) Is there a more elegant way of forcing Matlab to create a real copy of a variable?
No, the best you can do is try the assignment trick.

D Lang: std.bitmanip read data into struct

I found this function: https://dlang.org/phobos/std_bitmanip.html#.read
T read(T, Endian endianness = Endian.bigEndian, R)(ref R range)
if (canSwapEndianness!T && isInputRange!R && is(ElementType!R : const(ubyte)));
But I can't understand how to make reading from data into structure.
Is it possible to use something like this?
struct MyType {
uint value;
this(ubyte act) { // wtf.peek!ubyte();
switch (act) {
case 0:
value = wtf.read!uint();
break;
case 1:
wtf.read!ubyte(); // seek
value = wtf.read!uint() | 0x7;
break;
...
}
}
}
...
buffer.read!MyType();
I don't fully understand the structure of your code - where does wtf come from?
It seems to me you've misunderstood what to pass to MyType's constructor - you probably should pass it the range, and read off of that with std.bitmanip.read. I might have misunderstood your code, though:
import std.range;
import std.bitmanip;
struct MyType {
uint value;
this(R)(auto ref R rng) if (isInputRange!R && is(ElementType!R : const(ubyte)))
{
auto act = rng.peek!ubyte;
switch (act) {
case 0:
value = rng.read!uint;
break;
case 1:
rng.read!ubyte;
value = rng.read!uint;
break;
// ...
default: assert(0);
}
}
}
unittest {
ubyte[] buffer = [0x01,0x12,0x34,0x56,0x78];
auto a = MyType(buffer);
assert(buffer.length == 0);
assert(a.value == 0x12345678);
}
As you can see, I simply call MyType's constructor with the array as parameter, but you could also wrap it in a read function if you really want to:
alias read = std.bitmanip.read;
T read(T : MyType, R)(auto ref R range)
if (isInputRange!R && is(ElementType!R : const(ubyte)))
{
return MyType(range);
}
unittest {
ubyte[] buffer = [0x01,0x12,0x34,0x56,0x78];
auto a = buffer.read!MyType;
assert(buffer.length == 0);
assert(a.value == 0x12345678);
}

Using SQLFetchScroll to insert data

I'm trying to insert into a table data using SQLFetchScroll. The problem is if I set ROWSET_SIZE to 2, for example, of 1000 lines from source I only insert 500 lines (half) into destination table. I think the problem is with the function SQLExecute but I can't figure out how to set it right. Does anyone know how to solve this, please?
const SQLUSMALLINT ROWSET_SIZE = 2;
SQLSetStmtAttr(srcHStmt, SQL_ATTR_ROW_ARRAY_SIZE, (SQLPOINTER)ROWSET_SIZE, 0);
SQLLEN FetchOffset = 0;
while (TRUE)
{
srcReturn = SQLFetchScroll(srcHStmt, SQL_FD_FETCH_NEXT, FetchOffset);
if (srcReturn == SQL_NO_DATA_FOUND || srcReturn == SQL_ERROR)
{
if (srcReturn == SQL_ERROR) showError(SQL_HANDLE_DBC, srcHDBC);
break;
}
dstReturn = SQLExecute(dstHStmt);
}
Compiler: VS2015 / 64 bit machine.
When using SQL_FETCH_RELATIVE I'm having the same results.
SQLExecDirect(...);
SQLLEN FetchOffset = 2;
while (TRUE)
{
srcReturn = SQLFetchScroll(srcHStmt, SQL_FETCH_RELATIVE, FetchOffset);
if (srcReturn == SQL_NO_DATA_FOUND || srcReturn == SQL_ERROR)
{
if (srcReturn == SQL_ERROR) showError(SQL_HANDLE_DBC, srcHDBC);
break;
}
dstReturn = SQLExecute(dstHStmt);
}

GIF LZW decompression

I am trying to implement a simple Gif-Reader in c++.
I currently stuck with decompressing the Imagedata.
If an image includes a Clear Code my decompression algorithm fails.
After the Clear Code I rebuild the CodeTable reset the CodeSize to MinimumLzwCodeSize + 1.
Then I read the next code and add it to the indexstream. The problem is that after clearing, the next codes include values greater than the size of the current codetable.
For example the sample file from wikipedia: rotating-earth.gif has a code value of 262 but the GlobalColorTable is only 256. How do I handle this?
I implemented the lzw decompression according to gif spec..
here is the main code part of decompressing:
int prevCode = GetCode(ptr, offset, codeSize);
codeStream.push_back(prevCode);
while (true)
{
auto code = GetCode(ptr, offset, codeSize);
//
//Clear code
//
if (code == IndexClearCode)
{
//reset codesize
codeSize = blockA.LZWMinimumCodeSize + 1;
currentNodeValue = pow(2, codeSize) - 1;
//reset codeTable
codeTable.resize(colorTable.size() + 2);
//read next code
prevCode = GetCode(ptr, offset, codeSize);
codeStream.push_back(prevCode);
continue;
}
else if (code == IndexEndOfInformationCode)
break;
//exists in dictionary
if (codeTable.size() > code)
{
if (prevCode >= codeTable.size())
{
prevCode = code;
continue;
}
for (auto c : codeTable[code])
codeStream.push_back(c);
newEntry = codeTable[prevCode];
newEntry.push_back(codeTable[code][0]);
codeTable.push_back(newEntry);
prevCode = code;
if (codeTable.size() - 1 == currentNodeValue)
{
codeSize++;
currentNodeValue = pow(2, codeSize) - 1;
}
}
else
{
if (prevCode >= codeTable.size())
{
prevCode = code;
continue;
}
newEntry = codeTable[prevCode];
newEntry.push_back(codeTable[prevCode][0]);
for (auto c : newEntry)
codeStream.push_back(c);
codeTable.push_back(newEntry);
prevCode = codeTable.size() - 1;
if (codeTable.size() - 1 == currentNodeValue)
{
codeSize++;
currentNodeValue = pow(2, codeSize) - 1;
}
}
}
Found the solution.
It is called Deferred clear code. So when I check if the codeSize needs to be incremented I also need to check if the codeSize is already max(12), as it is possible to to get codes that are of the maximum Code Size. See spec-gif89a.txt.
if (codeTable.size() - 1 == currentNodeValue && codeSize < 12)
{
codeSize++;
currentNodeValue = (1 << codeSize) - 1;
}

How to avoid Switch Case & sscanf Function

I am working on C++. I am writing code for date format. Using with, we can get default date format from anyother date format. So I have found 240 date formats for this task. So M want to use switch case and sscanf function. Every case have sscanf function to separate day,month, year. So I need 240 cases and 240 sscanf function. Is there any method to avoid lot of swtich and sscanf? If you have any ideas, please let me know guys.
case 0:
sscanf(tsdate.c_str(),"%2d/%2d/%4d",&day,&month,&year);
break;
case 1:
sscanf(tsdate.c_str(),"%2d-%2d-%4d",&month,&day,&year);
break;
case 2:
sscanf(tsdate.c_str(),"%2d %2d %4d",&day,&month,&year);
break;
case 3:
sscanf(tsdate.c_str(),"%2d/%2d/%2d",&day,&month,&year);
coryear(year);
break;
case 4:
sscanf(tsdate.c_str(),"%2d/%2d/%2d",&year,&month,&day);
coryear(year);
break;
Like above, I want to put 240 cases and 240 sscanf. Please let me know how to avoid a lot of cases.
you can't avoid the switch cases but you can create multiple function to avoid sscanf:
void scanDayFirst(string format)
{
sscanf(tsdate.c_str(),format,&day,&month,&year);
break;
}
void scanMonthFirst(string format)
{
sscanf(tsdate.c_str(),format,&month,&day,&year);
break;
}
and so on.....
the result will be like this:
case 0:
scanDayFirst("%2d/%2d/%4d");
case 1:
scanDayFirst("%2d-%2d-%4d");
case 2:
scanDayFirst("%2d %2d %4d");
case 3:
scanDayFirst("%2d.%2d.%4d");
enum { ITEM_YEAR, ITEM_MONTH, ITEM_DAY, NUM_ITEMS };
struct date_format { char const *fmt; int items[NUM_ITEMS]; };
struct date_format const formats[] =
{ { "%2d/%2d/%4d", { ITEM_DAY, ITEM_MONTH, ITEM_YEAR } }
, { "%2d-%2d-%4d", { ITEM_MONTH, ITEM_DAY, ITEM_YEAR } }
/* etc. */
};
int parts[NUM_ITEMS]; /* Instead of year,month,day */
sscanf(tsdate.c_str(), formats[x].fmt,
&parts[formats[x].items[0]],
&parts[formats[x].items[1]],
&parts[formats[x].items[2]]);
You can extend this to have ITEM_NONE if you want to skip an item, or add extra items, and so on.
NB. If this is C++ then consider using stream input instead of sscanf.
Instead of enumerating all cases consider instead using a custom language for the format specification:
Date x = parseDate(user_input, "dd-mm-yyyy");
it will make the function shorter, easier to document and easier to use also improving the readability of the code that uses it. The idea is using codes like
yyyy ............. 4-digits year
yy ............... 2-digits year with automatic century computation
mm ............... 2-digits month
m ................ 1 or 2 digits month
dd ............... 2-digits day
d ................ 1 or 2 digits day
anything else .... mandatory character
A simple implementation could be
Date parseDate(const std::string& input, const std::string& format) {
const char *src = input.c_str();
const char *fmt = format.c_str();
int year=-1, month=-1, day=-1;
while (*fmt) {
if (!*src) throw invalid_date(input, format);
if (strncmp(fmt, "yyyy", 4) == 0) {
fmt += 4;
year = getInt(src, 4);
} else if (strncmp(fmt, "yy", 2) == 0) {
fmt += 2;
year = guess_century(getInt(src, 2));
} else if (strncmp(fmt, "mm", 2) == 0) {
fmt += 2;
month = getInt(src, 2);
} else if (fmt[0] == 'm') {
fmt += 1;
month = getInt(src, -1);
} else if (strncmp(fmt, "dd", 2) == 0) {
fmt += 2;
day = getInt(src, 2);
} else if (fmt[0] == 'd') {
fmt += 1;
day = getInt(src, -1);
} else {
if (src[0] != fmt[0]) throw invalid_date(input, format);
src++; fmt++;
}
}
if (*src || year == -1 || month == -1 || day == -1)
throw invalid_date(input, format);
return Date(year, month, day);
}
NOTE: untested