Okay, I've been working on my own version of a Date class and I had it working before but thought I'd try to revise the code a little at a time, after reading "Effective C++: Third Edition" by Scott Meyers

I'll start with the code as I'm not too sure how to explain what I want to do without showing it:

My "Date" class header file:

#include <iostream>
#include <string>
#include <iostream>
#include "dateimpl.h"

using namespace std;

class Date
{
  private:
    Month thisMonth;
    Day thisDay;
    Year thisYear;
  public:
    Date(const Month &m, const Day &d, const Year &y):thisMonth(m), thisDay(d), thisYear(y)
    {
      thisMonth.getStr();
      thisDay.getStr();
    }
};

...I left most of the code out, and only posted what's related to the issue I'm having

My "DateImpl" class header file:

#include <iostream>
#include <string>

using namespace std;

class Year
{
  public:
    int intVal;
    Year(int y):intVal(y) {}
};

class Month
{
  public:
    void getStr();
    const int intVal;
    const string strVal;
    Month(const int m):intVal(m) {}
};

class Day
{
  public:
    void getStr();
    const int intVal;
    const string strVal;
    Day(int d):intVal(d) {}
};

The getStr() functions in each of the above are independent, as are the variables...they're only pertinent to the Month and Day classes separately depending on what type they are. These functions are necessary to set "strVal" for each class, as there are calculations or conditionals needed to achieve this.

The issue I'm having is...how would I set the "thisMonth" and "thisDay" string values seemlessly within the "Date" class constructor...if possible?

When I try compiling the code above, the error I get is:

CMakeFiles/calendar2.dir/main.cpp.o: In function 'Date':
/home/josh/projects/Calendar2/./src/date.h:41: undefined reference to 'Month::getStr()'
/home/josh/projects/Calendar2/./src/date.h:42: undefined reference to 'Day::getStr()'

How would I achieve what I'm trying to do? Basically, have it so when a new instance of the Date class is created with the Month, Day, and Year initialized within the constructor, also set the Month and Day string values automatically.

Thanks in advance
-Josh

Do you have any implementation at all for the getStr() methods, or just the declarations? If you do have any implementation, it would be advisable to post it. The errors you mentioned generally indicate that the linker can't find the implementation of a function that it needs.

Edited 6 Years Ago by Fbody: n/a

Do you have any implementation at all for the getStr() methods, or just the declarations? If you do have any implementation, it would be advisable to post it. The errors you mentioned generally indicate that the linker can't find the implementation of a function that it needs.

Yep I have them...here's the "DateImpl" cpp file:

#include "dateimpl.h"

void Month::getStr()
{
  switch(Month::intVal)
  {
    case(1):
      strVal = "January";
    case(2):
      strVal = "February";
    case(3):
      strVal = "March";
    case(4):
      strVal = "April";
    case(5):
      strVal = "May";
    case(6):
      strVal = "June";
    case(7):
      strVal = "July";
    case(8):
      strVal = "August";
    case(9):
      strVal = "September";
    case(10):
      strVal = "October";
    case(11):
      strVal = "November";
    case(12):
      strVal = "December";
  }
}

void Day::getStr()
{
  string dayOfWeek[7] = {"Saturday", "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday"};
  int tempMonth = 0;
  if(Month::intVal >= 3)
    tempMonth = Month::intVal;
  else
    tempMonth = Month::intVal + 12;
  strVal = dayOfWeek[(Day::intVal + (((tempMonth+1)*26)/10 ) + Year::intVal + (Year::intVal/4) + 6*(Year::intVal/100) + (Year::intVal/400) )%7];
}

Did you compile and link DateImpl.cpp with the rest of your files? I don't see anything that would cause a link failure.

One thing that does concern me though:
None of your switch cases in Month::getStr() have break statements. As a result, no matter what the integer value of the month is, you will get "December" as the string value.

[edit]
Oops, hold on....
I just noticed that your string and integer values are declared as const. If you want to be able to change those values, you can't have them const. This could be part of the issue. By the time you are into the body of the Date constructor, those values are already "set in stone". From that point on, you can not change them.

Edited 6 Years Ago by Fbody: n/a

Did you compile and link DateImpl.cpp with the rest of your files? I don't see anything that would cause a link failure.

One thing that does concern me though:
None of your switch cases in Month::getStr() have break statements. As a result, no matter what the integer value of the month is, you will get "December" as the string value.

[edit]
Oops, hold on....
I just noticed that your string and integer values are declared as const. If you want to be able to change those values, you can't have them const. This could be part of the issue. By the time you are into the body of the Date constructor, those values are already "set in stone". From that point on, you can not change them.

That makes sense. I removed const from them, but still get the same error thought :(

And I just corrected the switch statement as well. Originally, I had the switch statement set up to return a string (the function returned a string) so the break wasn't necessary. But I changed it to set the value of strVal instead and forgot to add break to each case

As for linking...I'm using an IDE (KDevelop within Gentoo) and had assumed it took care of the linking for me, but I'm not sure. This is a new IDE for me (I was using NetBeans before) so I'm not too familiar with how it handles things

And it's Makefile is a mess to look through...variables all over, linking to various files telling it where to find information :-/

I'm sure it's got to be something simple I overlooked. I did rename the class files so it's possible the IDE got confused by it

I'll create a new class file for DateImpl and link to that one instead to see if it changes anything

Thanks for all your help :)

Could it be getting confused that I'm telling it to create a new class called DateImpl, but then I'm deleting the contents of it and using the file to declare 3 new classes instead? (Month, Day, Year)

Tried creating a new class file with a different name and it has the same effect

I'm not sure what I'm doing wrong exactly. Really curious, though, that's for sure

I rarely write up my own constructor, so I'm sure it's a mistake I'm making :)

As long as DateImpl.cpp is part of your current "project", most IDEs will take care of the linking for you...

Since you're on a Linux system, what specifically is the name of your header file? Is it "DateImpl.h" or "dateimpl.h"? The reason I ask is Linux filenames are case sensitive. You keep saying the file's name is "DateImpl", but you have #included "dateimpl.h" which is not the same. Try fixing the casing of your #include to match the actual casing of the filename.

As long as DateImpl.cpp is part of your current "project", most IDEs will take care of the linking for you...

Since you're on a Linux system, what specifically is the name of your header file? Is it "DateImpl.h" or "dateimpl.h"? The reason I ask is Linux filenames are case sensitive. You keep saying the file's name is "DateImpl", but you have #included "dateimpl.h" which is not the same. Try fixing the casing of your #include to match the actual casing of the filename.

KDevelop automatically set all filenames to lowercase for me, so it is "dateimpl.h" (and "dateimpl.cpp")

I tried to emulate a wrong filename by simply commenting out the header file and I got a much longer list of errors, including "Month thisMonth" being invalid so it seems to be finding the header file ok (as the error goes away when 'include "dateimpl.h"' is uncommendted out

Is it possible that it's an out-of-order issue? For example, does the constructor from my Date header file:

...
    Date(const Month &m, const Day &d, const Year &y):thisMonth(m), thisDay(d), thisYear(y)
    {
      thisMonth.getStr();
      thisDay.getStr();
    }
...

Does it set the value for thisMonth, thisDay, and thisYear to m,d, and y (respectively) before running the statements within the curly braces?

(hmm...tried testing it like shown below):

...
    Date(const Month &m, const Day &d, const Year &y)//:thisMonth(m), thisDay(d), thisYear(y)
    {
      thisMonth = m;
      thisDay = d;
      thisYear = y;
      thisMonth.getStr();
      thisDay.getStr();
    }
...

But this yielded the same results

And I realize what I did wrong above in my explanation...sorry for saying it was the DateImpl header and class files...as it's just a file dateimpl.h with classes within it

I'm making progress I think...

I decided to use the functions in the header file instead, which means you were right again. For some reason it must not have been accessing the cpp file from before
Here's the updated header file:

#include <iostream>
#include <string>

using namespace std;

class Year
{
  public:
    int intVal;
    explicit Year(int y):intVal(y) {}
    Year() { intVal = 0; };
};

class Month
{
  public:
    string strVal;
    int intVal;
    void getStr()
    {
      switch(Month::intVal)
      {
	case(1):
	  strVal = "January";
	  break;
	case(2):
	  strVal = "February";
	  break;
	case(3):
	  strVal = "March";
	  break;
	case(4):
	  strVal = "April";
	  break;
	case(5):
	  strVal = "May";
	  break;
	case(6):
	  strVal = "June";
	  break;
	case(7):
	  strVal = "July";
	  break;
	case(8):
	  strVal = "August";
	  break;
	case(9):
	  strVal = "September";
	  break;
	case(10):
	  strVal = "October";
	  break;
	case(11):
	  strVal = "November";
	  break;
	case(12):
	  strVal = "December";
	  break;
      }
    }
    explicit Month(int m):intVal(m) {}
};

class Day
{
  public:
    string strVal;
    int intVal;
    void getStr()
    {
      string dayOfWeek[7] = {"Saturday", "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday"};
      int tempMonth = 0;
      if(Month::intVal >= 3)
	tempMonth = Month::intVal;
      else
	tempMonth = Month::intVal + 12;
      strVal = dayOfWeek[(Day::intVal + (((tempMonth+1)*26)/10 ) + Year::intVal + (Year::intVal/4) + 6*(Year::intVal/100) + (Year::intVal/400) )%7];
    }
    explicit Day(int d):intVal(d) {}
};

And it no longer complains that getStr is undefined...

But now I get a new list of errors, which I had a feeling would happen...they're all regarding not being able to access the variables from the other classes within dateimpl.h...specifically here:

class Day
{
  public:
    string strVal;
    int intVal;
    void getStr()
    {
      string dayOfWeek[7] = {"Saturday", "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday"};
      int tempMonth = 0;
      if(Month::intVal >= 3)
	tempMonth = Month::intVal;
      else
	tempMonth = Month::intVal + 12;
      strVal = dayOfWeek[(Day::intVal + (((tempMonth+1)*26)/10 ) + Year::intVal + (Year::intVal/4) + 6*(Year::intVal/100) + (Year::intVal/400) )%7];
    }
    explicit Day(int d):intVal(d) {}
};

This one I'm really unsure how to correct. It's expecting an object to be instantiated for the Month and Year classes so it can access the variables, which makes sense now

Maybe I'm supposed to make these 3 classes reside in another class? So they can access each other's variables correctly? And this may explain why the cpp wasn't working either...

Edited 6 Years Ago by josolanes: n/a

I tried to emulate a wrong filename by simply commenting out the header file and I got a much longer list of errors, including "Month thisMonth" being invalid so it seems to be finding the header file ok (as the error goes away when 'include "dateimpl.h"' is uncommendted out

That makes it pretty certain that this dateimpl.cpp file is not included in the compilation. So you just have to find a way how to get that file included in the compilation. Unfortunately I don't know how it is done in KDevelop, but here is a link to the The User Manual to KDevelop: Projects.

[EDIT]
I see you've rearranged things.

Pertaining to the most recent problem; the Month::intVal syntax would only be used if the Month class would have a static member variable intVal i.e.

class Month
{
    static int IntVal;
    // .. or perhaps const static value
    static const int IntVal_2;
};

int Month::IntVal = 42;
const int Month::IntVal_2 = 42;

Edited 6 Years Ago by mitrmkar: EDIT

[edit]
NVM.... I need to get caught up.... took Waaaaaaaaaayyyyy too long to post...

Edited 6 Years Ago by Fbody: n/a

That makes it pretty certain that this dateimpl.cpp file is not included in the compilation. So you just have to find a way how to get that file included in the compilation. Unfortunately I don't know how it is done in KDevelop, but here is a link to the The User Manual to KDevelop: Projects.

[EDIT]
I see you've rearranged things.

Pertaining to the most recent problem; the Month::intVal syntax would only be used if the Month class would have a static member variable intVal i.e.

class Month
{
    static int IntVal;
    // .. or perhaps const static value
    static const int IntVal_2;
};

int Month::IntVal = 42;
const int Month::IntVal_2 = 42;

Ah that's true...as then I couldn't have separate instances of each variable, and that's no good since I want to have the ability to have multiple "Date" instances

What I've done, now, is move the

getStr()

functions out from the dateimpl.h and into the date.cpp (for my Date class).

This seems like it would allow me to write the functions within the date.cpp file as I've used

#include "dateimpl.h"

in the date.cpp now as well. It looks like this would allow me to use the Date instances to control which intVal I'm referencing

Here is how the classes are written within the date.cpp:

void Month::getStr()
{
  switch(Date.thisMonth.intVal)
  {
    case(1):
      Date.thisMonth.strVal = "January";
      break;
    case(2):
      Date.thisMonth.strVal = "February";
      break;
    case(3):
      Date.thisMonth.strVal = "March";
      break;
    case(4):
      Date.thisMonth.strVal = "April";
      break;
    case(5):
      Date.thisMonth.strVal = "May";
      break;
    case(6):
      Date.thisMonth.strVal = "June";
      break;
    case(7):
      Date.thisMonth.strVal = "July";
      break;
    case(8):
      Date.thisMonth.strVal = "August";
      break;
    case(9):
      Date.thisMonth.strVal = "September";
      break;
    case(10):
      Date.thisMonth.strVal = "October";
      break;
    case(11):
      Date.thisMonth.strVal = "November";
      break;
    case(12):
      Date.thisMonth.strVal = "December";
      break;
  }
}

void Day::getStr()
{
  string dayOfWeek[7] = {"Saturday", "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday"};
  int tempMonth = 0;
  if(Date.thisMonth.intVal >= 3)
    tempMonth = Date.thisMonth.intVal;
  else
    tempMonth = Date.thisMonth.intVal + 12;
  Date.thisDay.strVal = dayOfWeek[(Day::intVal + (((tempMonth+1)*26)/10 ) + Date.thisYear.intVal + (Date.thisYear.intVal/4) + 6*(Date.thisYear.intVal/100) + (Date.thisYear.intVal/400) )%7];
}

Wherever there was Month::intVal, for example, I replaced with Date.thisMonth.intVal. This seems like it should offer instance-specific variables for everything

However, I'm back to having the errors listed in the first post. Another linking issue. I'll look to see how to link them in KDevelop

Okay, maybe not the nicest way to correct the linking issue but I'm reading online that I could also:

#include "dateimpl.h"

Which now seems to put this whole thing in a weird loop...working on correcting that

Edited 6 Years Ago by josolanes: n/a

Okay, maybe not the nicest way to correct the linking issue but I'm reading online that I could also:

#include "dateimpl.h"

Which now seems to put this whole thing in a weird loop...working on correcting that

To correct the loop, I decided to combine the dateimpl.h and date.h files

date.h has always linked to date.cpp well, so I *think* this should be safe


EDIT: back to the original issue with the current setup

Edited 6 Years Ago by josolanes: n/a

now seems to put this whole thing in a weird loop...working on correcting that

I think your header files are missing include guards. So, first, add those in every header file you have, you won't be breaking anything even if you wind up with a setup that actually does not need include guards.

This article has been dead for over six months. Start a new discussion instead.