Skip to content

Commit 445ae79

Browse files
authored
Merge pull request #148 from pieterhijma/api-docs
Add an API doc section to C++ best practices
2 parents de001b2 + ce69bba commit 445ae79

File tree

2 files changed

+362
-1
lines changed

2 files changed

+362
-1
lines changed

bestpractices/c++practices.md

Lines changed: 361 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -610,3 +610,364 @@ constexpr std::initializer_list<Button> buttonDefs {
610610
```
611611
{% endraw %}
612612
When in doubt, use a struct - it is better to have good names than not.
613+
614+
## API Documentation
615+
616+
API Documentation in FreeCAD consists of two main types of documentation. **Topics** explain the concepts behind the code and **API documentation strings** explain the code itself. Both are important but topics are especially valued.
617+
618+
Although it is not strictly required, developers are highly encouraged to write API documentation when applicable. Note that it **is required** to make current API documentation up-to-date with changes that are made to the code. This section presents a set of guidelines for that documentation.
619+
620+
### General guidelines
621+
622+
- Be as complete as possible with each item (classes, enums, public and protected members) in a file having a documentation string.
623+
- Minimize the vertical space. So, when possible, use a single line with `///` or `///<` for enum items (see below).
624+
- In general, do not document C++ concepts such as destructors, friends, or constructors, except when there are arguments or if something special happens.
625+
626+
### Syntax
627+
628+
Doxygen supports two different types of syntax for commands, those with a backslash (e.g. `\brief`) and those with an `@` symbol (`@brief`). The `@`-prefixed version is more prevalent in the FreeCAD source code and is the preferred style for new documentation.
629+
630+
For a single line API documentation comment, for example commenting a function, use `/// <your comment.` without using `@brief` and ending with a full stop. Example:
631+
632+
```c++
633+
/// Get the transaction ID of this transaction.
634+
int getID() const;
635+
```
636+
637+
For multiple lines, use `/**` to start the docstring. The documentation starts on the next line with a `@brief` command that ends with a full stop and then a line break. In the case below, there are details after which the `@param[in]` and `@return` commands document the parameters and return statement, respectively.
638+
639+
```c++
640+
/**
641+
* @brief Check whether the passed name is valid.
642+
*
643+
* If a name is null or an empty string it is considered invalid, and valid
644+
* otherwise.
645+
*
646+
* @param[in] name The name to check.
647+
* @return True if the name is valid, false otherwise.
648+
*/
649+
static bool isValidName(const char* name);
650+
```
651+
652+
Keep the number of added lines to a minimum. For example, use `///<` to keep the comments on the same line:
653+
654+
```c++
655+
/// The status of the transaction object.
656+
enum Status
657+
{
658+
New, ///< A new object is added to the document.
659+
Del, ///< An object is deleted from the document.
660+
Chn ///< An object is changed in the document.
661+
} status {New};
662+
```
663+
664+
Use a single line to separate the documentation of different functions to make clearer to which function the documentation string belongs:
665+
666+
```c++
667+
/// Generate a new unique transaction ID.
668+
static int getNewID();
669+
670+
/// Get the last transaction ID.
671+
static int getLastID();
672+
```
673+
674+
To prevent Doxygen from automatically creating an irrelevant or undesired link use a `%` prefix. For example, since there is a namespace `Base` in FreeCAD, use `%Base` as shown in the example below to prevent Doxygen to create a link here to the `Base` package:
675+
676+
```c++
677+
/**
678+
* @brief %Base class of all properties.
679+
* @ingroup PropertyFramework
680+
*
681+
* This is the base class of all properties. Properties are objects that are
682+
* ...
683+
*/
684+
```
685+
686+
It is possible to use Markdown syntax in doc comments. For example, a Markdown list:
687+
688+
```c++
689+
/**
690+
* @brief A multi index container for holding the property spec.
691+
*
692+
* The multi index has the following index:
693+
* - a sequence, to preserve creation order
694+
* - hash index on property name
695+
* - hash index on property pointer offset
696+
*/
697+
mutable bmi::multi_index_container<
698+
PropertySpec,
699+
bmi::indexed_by<
700+
...
701+
> propertyData;
702+
```
703+
704+
### Placing API documentation
705+
706+
The main page of the API documentation is in `src/Doc/mainpage.dox.in`. The most important function of this page is the "Organization of the API Documentation" section allowing readers to quickly browse important topics.
707+
708+
Topics should be defined in dedicated `.dox` files in the main directory of a package. For example, `src/App/core-app.dox` defines a group `APP` that is in group `CORE` (defined in the main page) and then it defines several topics in `APP`, such as `DocumentGroup` (for the concept of a Document in FreeCAD), `DocumentObjectGroup` (for the concept of a document object), etc. Below are two excerpts from that file to give an indication:
709+
710+
```c++
711+
/**
712+
* @defgroup APP App
713+
* @ingroup CORE
714+
* @brief The part of FreeCAD that works without GUI (console or server mode).
715+
*
716+
* It contains the App namespace and defines core concepts such as
717+
* @ref DocumentGroup "Document", @ref DocumentObjectGroup "Document Object",
718+
* @ref PropertyFramework "Property Framework", @ref ExpressionFramework
719+
* "Expression Framework", and the @ref ExtensionFramework "Extension
720+
* Framework".
721+
*
722+
* The largest difference between the functionality in @ref BASE "Base"
723+
* compared to %App is that %App introduces the notion of properties, both used
724+
* ...
725+
*/
726+
```
727+
728+
```c++
729+
/**
730+
* @defgroup DocumentGroup Document
731+
* @ingroup APP
732+
* @brief The class that represents a FreeCAD document
733+
*
734+
* This (besides the App::Application class) is the most important class in FreeCAD.
735+
* It contains all the data of the opened, saved, or newly created FreeCAD Document.
736+
* The App::Document manages the Undo and Redo mechanism and the linking of documents.
737+
* ...
738+
*/
739+
```
740+
741+
Given such a topic, you can include the most important classes into this topic, for example in `App/Document.h` class `App::Document` is included in `DocumentGroup`.
742+
743+
```c++
744+
/**
745+
* @brief A class that represents a FreeCAD document.
746+
*
747+
* A document is a container for all objects that are part of a FreeCAD
748+
* project. Besides managing the objects, it also maintains properties itself.
749+
* As such, it is also a PropertyContainer.
750+
*
751+
* @ingroup DocumentGroup
752+
* For a more high-level discussion see the topic @ref DocumentGroup "Document".
753+
*/
754+
class AppExport Document: public PropertyContainer
755+
{
756+
...
757+
```
758+
759+
Documentation strings can be added to classes, enums, and public and protected members of a class. This is especially important if a class is at the top of a hierarchy and its methods are reused in many other subclasses. The documentation should be added in the header file and not in the C++ file.
760+
761+
### Specific guidelines
762+
763+
This section contains guidelines for specific types of documentation.
764+
765+
#### Namespaces
766+
767+
Since namespace statements are typically in many files, it is difficult to determine where to put documentation strings. In most cases a best practice is to add them to the `.dox` file of the respective module. For example, in `src/App/core-app.dox`:
768+
769+
```c++
770+
/**
771+
* @namespace App
772+
* @ingroup APP
773+
* @brief The namespace for the part of FreeCAD that works without GUI.
774+
*
775+
* This namespace includes %Application services of FreeCAD that such as:
776+
* - The Application class
777+
* - The Document class
778+
* - The DocumentObject classes
779+
* - The Expression classes
780+
* - The Property classes
781+
*
782+
* For a more high-level discussion see the topic @ref APP "App".
783+
*/
784+
```
785+
786+
#### Classes
787+
788+
For classes use a brief statement and more detailed comments:
789+
790+
```c++
791+
/**
792+
* @brief A class that represents a FreeCAD document.
793+
*
794+
* A document is a container for all objects that are part of a FreeCAD
795+
* project. Besides managing the objects, it also maintains properties itself.
796+
* As such, it is also a PropertyContainer.
797+
*
798+
* ...
799+
*/
800+
class AppExport Document: public PropertyContainer
801+
{
802+
...
803+
```
804+
805+
#### Functions
806+
807+
For functions, use a brief statement, optionally more detailed comments, and if applicable and the parameters are not trivial, `@param` statements marking the direction `[in]`, `[out]`, or `[in,out]`, a `@return` command and `@throws` command, if applicable. An example:
808+
809+
```c++
810+
/**
811+
* @brief Get the value of the property identified by the path.
812+
*
813+
* This function gets the value of the property identified by the path. It
814+
* is meant to be overridden for subclasses in which the `path` is
815+
* typically ignored. The default implementation makes use of the `path`
816+
* ObjectIdentifier to get the value of the property.
817+
*
818+
* @param[in] path The path to the property.
819+
* @return The value of the property.
820+
*/
821+
virtual const boost::any getPathValue(const App::ObjectIdentifier& path) const;
822+
```
823+
824+
#### Enums
825+
826+
For enumerations, it often suffices to have a brief statement and a short comment for each item:
827+
828+
```c++
829+
/// The status of the transaction object.
830+
enum Status
831+
{
832+
New, ///< A new object is added to the document.
833+
Del, ///< An object is deleted from the document.
834+
Chn ///< An object is changed in the document.
835+
} status {New};
836+
```
837+
838+
#### Grouping
839+
840+
Special documentation comments can be used to indicate that some group of functions, variables, etc. are all part of some single conceptual grouping. A best practice is to start a group with a documentation block `/**` with an `@name` command and an `@{` grouping command on the next line. The group is ended with `/// @}`.
841+
842+
Often, FreeCAD source files contain grouping commands with normal comments instead of documentation comments, such as `// @{` and `// @}`. Since these comments are not documentation comments, this usage is incorrect and can lead to issues in rendering the documentation. This usage should be avoided in new code, and corrected when encountered.
843+
844+
845+
```c++
846+
/**
847+
* @name Properties
848+
* @{
849+
*/
850+
851+
/// The long name of the document (utf-8 coded).
852+
PropertyString Label;
853+
854+
/// The fully qualified (with path) file name (utf-8 coded).
855+
PropertyString FileName;
856+
857+
...
858+
859+
/// Whether to use hasher on topological naming.
860+
PropertyBool UseHasher;
861+
/// @}
862+
```
863+
864+
#### Templates
865+
866+
Templates can be document with a `@tparam` command:
867+
868+
```c++
869+
/**
870+
* @brief Get all objects of a given type.
871+
*
872+
* @tparam T The type to search for.
873+
* @return A vector of objects of the given type.
874+
*/
875+
template<typename T>
876+
inline std::vector<T*> getObjectsOfType() const;
877+
```
878+
879+
#### Documentation with much repetition
880+
881+
The preferred way to handle code with significant repetition is to use `@copydoc` or `@copydetails`.
882+
883+
```c++
884+
/**
885+
* @brief Check if this mapped name starts with the search target.
886+
*
887+
* If there is a postfix, only the postfix is considered. If not, then only
888+
* the data is considered. A search string that overlaps the two will not
889+
* be found.
890+
*
891+
* @param[in] searchTarget The search target to match.
892+
* @param[in] offset An offset to perform the match at.
893+
*
894+
* @return True if this MappedName begins with the target bytes.
895+
*/
896+
bool startsWith(const QByteArray& searchTarget, int offset = 0) const;
897+
898+
/// @copydoc startsWith(const QByteArray&,int) const
899+
bool startsWith(const char* searchTarget, int offset = 0) const;
900+
901+
/// @copydoc startsWith(const QByteArray&,int) const
902+
bool startsWith(const std::string& searchTarget, int offset = 0) const;
903+
```
904+
905+
Another example that adds a parameter:
906+
907+
```c++
908+
/**
909+
* @brief Convert the expression to a string.
910+
*
911+
* @param[in] persistent If true, the string representation is persistent
912+
* and can be saved to a file.
913+
* @param[in] checkPriority If true, check whether the expression requires
914+
* parentheses based on operator priority.
915+
* @param[in] indent The indentation level for pretty-printing.
916+
*
917+
* @return The string representation of the expression.
918+
*/
919+
std::string toString(bool persistent = false, bool checkPriority = false, int indent = 0) const;
920+
921+
/**
922+
* @brief Write a string representation of the expression to a stream.
923+
*
924+
* @param[in,out] os The output stream to write to.
925+
* @copydoc Expression::toString(bool, bool, int) const
926+
*/
927+
void toString(std::ostream &os, bool persistent=false, bool checkPriority=false, int indent=0) const;
928+
```
929+
930+
An example with `@copydetails`:
931+
932+
```c++
933+
/**
934+
* @brief Register an import filetype given a filter.
935+
*
936+
* An example of a filter is: @c "STEP with colors (*.step *.STEP *.stp *.STP)".
937+
* An example of a module is: @c "Import" or @c "Mesh".
938+
*
939+
* @param[in] filter The filter that describes the file type and extensions.
940+
* @param[in] moduleName The module name that can handle this file type.
941+
*/
942+
void addImportType(const char* filter, const char* moduleName);
943+
944+
/**
945+
* @brief Register an export filetype given a filter.
946+
*
947+
* @copydetails addImportType
948+
*/
949+
void addExportType(const char* filter, const char* moduleName);
950+
```
951+
952+
Another way to handle documentation with a high degree of repetition is by grouping, although in most cases it is a best practice to use `@copydoc` or `@copydetails` instead.
953+
954+
```c++
955+
/**
956+
* @name PropertyData accessors
957+
*
958+
* @details These methods are used to access the property data based on:
959+
* - the offset base,
960+
* - either:
961+
* - a property pointer, or
962+
* - a property name.
963+
* @{
964+
*/
965+
const char* getName (OffsetBase offsetBase,const Property* prop) const;
966+
short getType (OffsetBase offsetBase,const Property* prop) const;
967+
short getType (OffsetBase offsetBase,const char* name) const;
968+
const char* getGroup (OffsetBase offsetBase,const char* name) const;
969+
const char* getGroup (OffsetBase offsetBase,const Property* prop) const;
970+
const char* getDocumentation(OffsetBase offsetBase,const char* name) const;
971+
const char* getDocumentation(OffsetBase offsetBase,const Property* prop) const;
972+
/// @}
973+
```

technical/codesigning.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ The complete process is:
6363
* `P12_PASSWORD` -- The password you set when exporting the certificate to the `*.p12` file.
6464
* `SIGNING_KEY_ID` -- The ID of the certificate associated with `BUILD_PROVISION_PROFILE_BASE64`. Obtained by running `security find-identity -v -p codesigning` on a machine with the key installed. It is the SHA hash of the selected key in the displayed list.
6565
16. Run the "Weekly Build" GitHub action to generate the signed macOS weeklies based on these settings.
66-
66+
6767
## Windows
6868

6969
TBD

0 commit comments

Comments
 (0)