I have long been interested in the topic of commenting code.
This post motivated me to formalize my ideology of documenting code.
On the one hand, comments like this are correct, on the other I do not see much sense in them. Moreover, those programming books that I read somehow omit this moment or at least do not attach much importance to comments. I want to state my thoughts on why there is no sense to describe all the descriptions of the parameters of functions, methods and properties of classes, etc.
The words "describe all descriptions," I wrote it is no coincidence, because it is almost the same as
std::map<std::string, Person*>mapPPersonsNames
How did I get to such a heretical life?
When I was small, I learned how to program in Quick Basic and REXX. I did not disdain to call variables a, aa, x1. Then it became clear to me that this is definitely a bad idea, because the next day nothing is clear. I began to call variables a little more meaningfully, for example money, answer, file_count. At some point I decided to program the punto switcher for OS / 2. Naturally, it was necessary to study the API, and then I properly got into the spirit of self-documenting code. The names of the variables and the names of the functions in the OS / 2 API are so elaborated that sometimes it was not even necessary to read the descriptions. It was enough to see how they are designated parameters. I had an idea, why not transfer all the information from the comments directly into the code?
')
Why do we need comments in the code?
This is the key issue in this post. I see two fairly independent uses. First, you need to somehow understand what these or other elements are doing in order to continue working after some time or correct mistakes. And this also applies to joint development, when colleagues need to understand what and how. Another use is to tell library users how to use exported symbols, classes, patterns.
I am writing self-documenting code because I don’t want to waste time on duplicate work.
I slowly move the project in his spare time, in the mood. Breaks in the development are more than a month, and the volume is decent. I have no problems with refactoring some parts, because there is enough information in the code. If you call variables and functions incomprehensible, even with comments it will be difficult to figure out what is happening.
... here it is necessary to make the counter of the cycle i_1, because in the external cycle i ...
Damn it i_1 this is not in rows but in columns ...
Familiar with this situation? I hope not, because it is not difficult to bypass the loop by uiRowCount and uiColCount. But in this case, the meaning in the description of these variables disappears.
There is a statement that the self-documenting code should be broken down into a large number of functions. This code is difficult to understand and execution slows down. It is better to unfold the good old footwoman, season it with comments and read like Tolstoy's novel.
Old footcloth - a very dubious pleasure. It's not about readability, but about ease of processing and correcting errors. In addition, factorized code is better in terms of readability. Functions - the next level of detail of the algorithm. From the title it should be clear what they are doing. Usually there is no need to study the algorithm in detail. And if you are interested in a certain moment, you can see the body of the function. Do many functions slow down execution? Use inline. At the same time, I have functions for more than a hundred lines and there are no problems with readability.
What about libraries?
Surely you need to write documentation for library functions. From the user's point of view, it is better that the documentation be in a separate file. Why at the same time also comment on all the code? All the nuances must be made in the description of the library, unless of course there is no sadistic background. More than once I came across a hint in the description “well, look in the code, everything is there.” In fact, this means that in addition to RPM with the library, you need to download the source code. It is necessary to find the function and read the comments in the header and in the code itself. For the next function is the same. grep -R still no one canceled.
Unpleasant situation, is not it? At the same time, a separate file will be distributed with binaries and the user will only say thank you. By the way, the same applies to the description of the architecture or class hierarchy. If there are complex chains, why not describe them in a separate file?
UPDAt the request of the community cite a sample code. struct HGenStuff: public std::unary_function<const std::pair<wxString,int>&, void>{ std::map<wxString, DevDesc*>& mapDevs; std::map<wxString, LogDesc*>& mapLogs; HandlerLibData & hData; HGenStuff(std::map<wxString, DevDesc*>&_mapDevs, std::map<wxString, LogDesc*>&_mapLogs,HandlerLibData&_hData): mapDevs(_mapDevs), mapLogs(_mapLogs), hData(_hData) {}; inline void operator()(const std::pair<wxString,TypeFlag>&paInp){ if(paInp.second.isSet(HandlerLibData::DEVICE)){ std::map<std::string,DevInterface*>::iterator itDev; if((itDev = hData.HLI.mapDevs.find(std::string(paInp.first.mb_str())))!=hData.HLI.mapDevs.end()){ mapDevs.insert(std::make_pair(paInp.first, new DevDesc(itDev->second, &hData))); } } if(paInp.second.isSet(HandlerLibData::LOGGER)){ std::map<std::string,Logger *>::iterator itLog; if((itLog = hData.HLI.mapLogs.find(std::string(paInp.first.mb_str())))!=hData.HLI.mapLogs.end()){ mapLogs.insert(std::make_pair(paInp.first, new LogDesc(itLog->second, &hData))); } } } }; struct HandlerActivator: public std::unary_function<HandlerLibData&, void>{ HandlerBroker *pHB; std::map<wxString, DevDesc*>mapDevs; std::map<wxString, LogDesc*>mapLogs; std::map<wxString,std::map<wxString,TypeFlag> > &toUse; bool &bSuccess; HandlerActivator(HandlerBroker *_pHB, std::map<wxString,std::map<wxString,TypeFlag> > &_toUse, bool &_bSuccess):pHB(_pHB), toUse(_toUse), bSuccess(_bSuccess){ }; inline void operator()(HandlerLibData&inp){ std::map<wxString,std::map<wxString,TypeFlag> >::iterator it(toUse.find(inp.md5)); if(it != toUse.end()) std::for_each(it->second.begin(),it->second.end(),HGenStuff(mapDevs,mapLogs , inp)); } ~HandlerActivator(){ if (!pHB->checkLock(mapDevs)) bSuccess = wxYES == wxMessageBox(wxT("Some locks seam to be incompatible. Shall we use new device handlers selection?\n"),wxT("Confirm"), wxYES_NO | wxICON_EXCLAMATION ); if(bSuccess ) pHB->setAvailHandlers(mapDevs, mapLogs); }; }; bool MyFrame::pocessHandlers(std::map<wxString, std::map<wxString,TypeFlag> > &mapHandlersToUse){ std::set<HandlerLibData>::iterator it = strIntConf.setHandlerLibs.begin(); std::set<HandlerLibData>::iterator itEnd = strIntConf.setHandlerLibs.end(); bool bSuccess = true; HandlerActivator hGen(fGetHBroker(),mapHandlersToUse, bSuccess); while(it != itEnd) hGen(*const_cast<HandlerLibData*>(&(*it++))); return bSuccess; } void MyFrame::loadHandlers(){ wxDynamicLibrary dllDev; wxDir dirHandlers(strIntConf.sHandlersDir); wxString sLibName; if (!dirHandlers.GetFirst( &sLibName, wxEmptyString, wxDIR_FILES)) return; do { MD5 md5Op; wxString sFullPath = strIntConf.sHandlersDir + wxFileName::GetPathSeparator() +sLibName; wxString md5 = wxString(md5Op.digestFile((sFullPath).mb_str(wxConvLibc)),wxConvUTF8 ); if (strIntConf.setHandlerLibs.find(HandlerLibData(md5))!=strIntConf.setHandlerLibs.end()) continue; if(!dllDev.Load( sFullPath )) wxLogError(wxString::Format(wxT("Error Loading ")) + sLibName); else{ void (*dynLoad)(HandlerLibInterface *) = reinterpret_cast<void (*)(HandlerLibInterface* )>(dllDev.GetSymbol(wxT("dynLoad"))); if(dynLoad) { HandlerLibInterface HLI; dynLoad(&HLI); strIntConf.setHandlerLibs.insert( HandlerLibData( md5, sLibName, strIntConf.sHandlersDir, HLI,dllDev.Detach())); }else dllDev.Unload(); } }while(dirHandlers.GetNext( &sLibName)); }