The question is more a Best way of doing question than a real problem.
Context and what I'm doing
I have an application that must access to the database. Therefore I have a class DatabaseManager that is called every time I need to access the database and which does for instance:
DatabaseManager *db = new DatabaseManager;
std::vector<Element> elementVector = db->getAElementsById(id);
And you the same for all insert, update or remove requests.
What eventually happened...
...is that my file has already 1200 lines and is growing with every new feature.
The question
So how do pretty people do ?
One header several splitted files? (This told that it's not a good idea)
Splitting the class in several class? The databaseManager is one class, it would be a nonsense...
Is there any other option? (I'm not familiar with the notion of namespace, maybe is it a way?)
I read also other post but didn't find either the good question to ask google, or people answering my question.
I have my database module split into more than one file. For example, one file handles insertions, another file handles extractions and a third handles searches.
Mine is actually more complex, but the practice should be to split up the code into separate themed files, preferably no more than 300 LOC (Lines Of Code).
The solution I chose for now is the following (because I was too lazy to reconstruct new classes and so on) : I splitted the class in several cpp files
// DatabaseManager.h
class DatabaseManager
{
//// Getters - in DatabaseManager_getters.cpp
public:
// Movies
Movie getOneMovieById(const int id);
QList<Movie> getAllMovies(const QString fieldOrder = "title");
....
private:
// Other functions for getters
Movie hydrateMovie(QSqlQuery &query);
//// Inserts - in DatabaseManager_insert.cpp
public:
bool insertNewMovie(Movie &movie);
....
private:
bool insertNewTag(Tag &tag);
....
//// Updates - in DatabaseManager_update.cpp
public:
bool updateMovie(Movie &movie);
....
//// Delete - in DatabaseManager_delete.cpp
public:
bool deleteMovie(Movie &movie);
private:
bool deletePeople(const People &people);
};
And obviously the .cpp files are following what announced. It's easier for me to find where a function is and to maintain the code.
Another time, I'll think twice if it's not more relevant to follow the advices you gave.
Thx again !
Related
I'm trying to create simple game in C++. At one point I want to have some setting, save and load from config file.
The config file should be read from the beginning, and should be accessible anywhere it needed.
So far I only see Singleton pattern as a solution.
Another way is to create an object an pass it down, but it can mess
up the current code.
I've also search and found something called Dependency Injection.
Is dependency injection useful in C++
Which design patterns can be applied to the configuration settings problem?
But I don't quite understand it, you still have to create an object in main and pass it down, right?
Singleton is quite simple, but some consider it antipattern, while pass it down the tree can mess up my current code. Is there any other Patterns?
P/S: I'm also curious how games load their setting.
I would suggest something simple as the following example, which circumvents any singleton-related or initialization order issue:
struct global_state
{
config _config;
};
struct game_state
{
global_state& _global_state;
};
int main()
{
global_state globals{load_config_from_file()};
game_state game{globals};
game.run();
}
Since _global_state is a member of game_state, it can be used in member functions without the need of explicitly passing it as a parameter:
void game_state::update_ui()
{
const float text_size = _global_state._config.get_float("text_size");
_some_text.set_size(text_size);
}
I am trying to understand project structure in c++, I am finding it difficult to get my head around class structure and header files.
Extract from article 1 (linked at bottom of this post)
By convention, include directory is for header files, but modern practice > suggests that include directory must strictly contain headers that need
to be exposed publicly.
My first question of this process is with regards to a separate class file that is within the include directory.
What is purpose of exposing your headers?
Following on from this, looking at an example of an exposed header file. Linked in the following GH repo: https://github.com/AakashMallik/sample_cmake
How does the Game_Interface class relate back to the Game_Engine?
game_interface.h
#pragma once
#include <game_engine.h>
class GameInterface
{
private:
GameEngine *game;
public:
GameInterface(int length);
void play(int num);
};
I have looked else where for a simple explanation of this process but, all I have found so far is nothing that can be understood in the context of this example.
Fairly new to C++ background in web technologies.
Link to article 1: https://medium.com/heuristics/c-application-development-part-1-project-structure-454b00f9eddc
What is purpose of exposing your headers?
Sometimes you may be developing some functionality or a library. You might want to help some other person or customer or client by sharing the functionality of your code. But you don't want to share the exact working details.
So for instance you wish to share an Image processing functionality which applies beautiful filters to it. But at the same time you don't want them to exactly know how did you implement it. for such scenarios, you can create a header file, say img_filter.h having function declaration -
bool ApplyFilter(const string & image_path);
Now you can implement entire details in img_filter.cpp:
bool ApplyFilter(const string & image_path)
{
....
// Implementation detail
...
}
Next you can prepare a dll of this file which could be used by your client. For reference of working, parameters, usage etc. you can share the img_filter.h.
Relation with Interface:
A well defined interface is generally nice to have so you can change implementation details transparently, which means, that HOW you implement the details don't matter as long as the interface or the function name and parameters are kept intact.
I'm trying to build a class that inherits from another class and initializes itself by parsing a text file. So I have:
On my header file (both in same file):
class Mother{
public:
Mother();
Mother(std::string path);
int parse_textfile(std::string path);
protected:
std::string path;};
class Daughter : public Mother {
public:
Daughter();
Daughter(path);
Daughter(std::string TITLE);
protected:
std::string path
std::string TITLE
Now, on my cpp file:
Mother::Mother(){};
Mother::Mother(string path)
:path(path)
{
parse_textfile(path);
};
Mother::parse_textfile(string path)
{
cout << "Processing file: " << path << endl;
ifstream file(path.c_str());
if (! file){
cerr << "unable to open input file: " << grid_path << " --bailing out! \n";
return -1;
}else{
string TITLE;
getline(file, TITLE);
return Daughter(TITLE)
Now, I understand that this code is not correct (obviously, as it won't compile without errors), but I am still so raw with C++ that some expert advice would really come a long way at this point.
The objective is to create a Daughter class that inherits the parsers, because the Daughter class itself should parse the textfile and hold all the variables in it. I will do line by line parsing because there is a lot of information in the text file. I just want this information to be held on the Daughter object so I can operate over this information later on.
Also, I'm using C++ mainly because I don't want to share the source code. If there is a more appropriate language, I would also like to hear it from you. This would be trivial for me in Python, but again, I don't want to share what's happening.
EDIT 1:
Based on what I got from the comments, I can start seeing more clearly what the specific question should be.
I have 3 text files that belong to the same problem, but each of this text files contain different information.
What I want is to get recommendations on how to initialize each of this text files as individual objects that are derived from the same "Mother class". Maybe it makes more sense that the parser actually belongs to each of the Daughters instead of the Mother. Either way, for those whose comments have been geared towards actually answering the question and not asking me "why I want to do this", I appreciate your help and recommendations.
Your issue is not about C++ itself, it's about software architecture & design. Could you try to extract "parser" functionality into separate class (let name it "factory"). Given that, you have someone who decides which instance could be created - Mother, Daughter, whatever. And you have a class hierarchy Mother-Daughter-etc which can share necessary common code and override some specific behaviors if needed.
I understand this can be named "overkill" but I'd recommend you so called "Gang of four" book named Design patterns. Here it seems to be public version of the PDF:
https://github.com/dieforfree/edsebooks/blob/master/ebooks/Design%20Patterns%2C%20Elements%20of%20Reusable%20Object-Oriented%20Software.pdf
My question is relatively simple: how would I go about implementing a UML sequence diagram in C++ code? I was reading up on sequence diagrams the other day, and I found this example for a program for a student enrolling in a seminar.
How would I go about turning this diagram into a program? For the sake of this question, lets focus on one class, say the EnrollInSeminar controller. How would I go about implementing this?
I imagine that it might be something like this:
class EnrollInSeminar
{
public:
void Activate();
};
void EnrollInSeminar::Activate()
{
SecurityLogon logonUI{};
Student theStudent = logonUI.getStudent();
SeminarSelector seminarSelectorUI{};
Seminar seminar = seminarSelectorUI.getSeminar();
if (!seminar.isEligible(theStudent))
return;
theStudent.getSchedule().determineFit(seminar);
Fee fee = StudentFees.calculateFees(seminar, theStudent);
FeeDisplay feeUI{fee};
if (!feeUI.getVerification())
return;
seminar.enrollStudent(theStudent);
}
Is this the correct way to implement the EnrollInSeminar class? If not, how should I do it?
Actually a SD does not tell anything about the methods being used in the messages passed from one object to another except the name, the parameters and - as the name says - the sequence. So the only thing you can draw from "just the SD" are methods and their parameters.
You will need additional information from a use case to know what the methods are all about. Without you simply can not "implement a SD".
In joint with this question. I am having trouble coming up with a good type safe solution to the following seemingly basic problem. I have a class music_playlist that has a list of the songs it should play. Seems pretty simple right, just make an std::list of all the songs in queue, and make it available to the user. However, out of necessity the audio decoding, and the audio rendering happens on separate threads. So the list needs to be mutex protected. Often times the mutex was forgotten by other programmers using my library. Which obviously led to "strange" problems.
So at first I just wrote setters for the class.
struct music{};
class music_playlist{
private:
std::list<music> playlist;
public:
void add_song(music &song){playlist.push_back(song);}
void clear(){playlist.clear();}
void set_list(std::list<music> &songs){playlist.assign(songs.begin(),songs.end());}
//etc
};
This led to user code like below...
music song1;
music song2;
music song3;
music song4;
music song5;
music song6;
music_playlist playlist1;
playlist1.add_song(song1);
playlist1.add_song(song2);
playlist1.add_song(song3);
playlist1.add_song(song4);
playlist1.add_song(song5);
playlist1.add_song(song6);
//or
music_playlist playlist2;
std::list<music> songs;
songs.push_back(song1);
songs.push_back(song2);
songs.push_back(song3);
songs.push_back(song3);
songs.push_back(song5);
songs.push_back(song6);
playlist2.set_list(songs);
While this works and is very explicit. It is very tedious to type out and it is bug prone due to all the cruft around the actual work. To demonstrate this, I actually intentionally put a bug into the above code, something like this would be easy to make and would probably go through code reviews untouched, while song4 never plays in playlist 2.
From there I went to look into variadic functions.
struct music{};
class music_playlist{
private:
std::list<music> playlist;
public:
void set_listA(music *first,...){
//Not guaranteed to work, but usually does... bleh
va_list Arguments;
va_start(Arguments, first);
if (first) {
playlist.push_back(first);
}
while (first) {
music * a = va_arg(Arguments, music*);
if (a) {
playlist.push_back(a);
}else {
break;
}
}
}
void set_listB(int count,music first,...){
va_list Arguments;
va_start(Arguments, first);
playlist.push_back(first);
while (--count) {
music a = va_arg(Arguments, music);
playlist.push_back(a);
}
}
//etc
};
Which would lead to a users code like below...
playlist1.set_listA(&song1,&song2,&song3,&song4,&song5,&song6,NULL);
//playlist1.set_listA(&song1,&song2,&song3,&song4,&song5,&song6); runtime error!!
//or
playlist2.set_listB(6,song1,song2,song3,song4,song5,song6);
Now its much easier to see if a song was added twice or not included. However in solution A it will crash if NULL is not at the end of the list, and is not cross platform. In solutionB you have to count the amount of arguments which can lead to several bugs. Also, none of the solutions are type safe, and the user could pass in an unrelated type and wait for the crash at runtime. This didn't seem to be a sustainable solution. So then I came across std::initializer_list. Can't use it several of the compilers I develop for don't have support for it yet. So I tried to emulate it. I ended up with this solution below.
Is this use of the "," operator considered bad form?
Which would lead to a users code looking like this...
struct music_playlist{
list<music> queue;
//...
};
int main (int argc, const char * argv[])
{
music_playlist playlist;
music song1;
music song2;
music song3;
music song4;
playlist.queue = song1,song2; // The queue now contains song1 and song2
playlist.queue+= song1,song3,song4; //The queue now contains two song1s and song2-4
playlist.queue = song2; //the queue now only contains song2
return 0;
}
This syntax was not confusing to our small testgroup. However, I had serious concerns with abusing operator overloading so severely. So I posted the question above. I wanted to see what programmers that were comparatively experts to our test group thought. Quite alot of programmers did not like it however it seemed better than the above solutions because it would catch most bugs at compile time instead of just at run time. However Tom posted an interesting counter example, that would cause unexpected behavior.
//lets combine differnt playlists
new_playlist.queue = song1 //the first playlist
,(song3,song4) //the second playlist //opps, I didn't add song 3!
, song5;
This sours me to that solution. So do you have any ideas about a better solution?
P.S. None of the above code was compiled, they are just there for example purposes.
The first question that comes to mind is whether this is a problem, and whether this is a problem worth the solution. Since you are creating an interface, your client will be user code (not people, code). How many times will your clients hardcode playlists in the code versus say, store load them from a file or construct them from their user selections?
Think on the actual value of the feature and compare that with the impact that it will have in how they use your library, and think on how many problems your users might have with the changed interface.
The simplest solution is accepting iterators to construct/reset your list, and then let the users deal with the problem. Of course they can build their own container in the way that you have shown, but they can also use Boost.Assignment to take care of the boiler plate, so their user code will look like:
std::vector<music> songs = boost::assign::list_of()( song1 )( song2 );
play_list.set( songs.begin(), songs.end() );
Or if they are not comfortable with that library the can use a plain old array:
music songs[2] = { song1, song2 };
play_list:set( songs, songs + 2 ); // add your preferred magic to calculate the size