6
\$\begingroup\$

I wrote a simple to use C++ INI file parser. The only thing it requires is the STD libs (of course that come with most major C++ compilers) So far it works very well, and the only thing that needs work is the string parser to handle special characters in the key's value. I.E \", \ \n \r.

You can view the code here:

awesome_ini.h

#ifndef AWESOME_INI_H
    #define AWESOME_INI_H

    /*
    AUTHOR: Andrew McRobb

    DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
    Version 2, December 2004

    Copyright (C) 2015 Andrew McRobb <andrewmcrobb@gmail.com>

    Everyone is permitted to copy and distribute verbatim or modified
    copies of this license document, and changing it is allowed as long
    as the name is changed.

    DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
    TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

    0. You just DO WHAT THE FUCK YOU WANT TO.

    */

    #include <stdlib.h>
    #include <iostream>
    #include <string.h>
    #include <ctype.h>
    #include <vector>
    #include <fstream>

    //Options you can set...
    #define AWI_SEPERATOR_CHAR '='
    #define AWI_COMMENT_CHAR ';'
    #define AWI_STRING_CHAR '"'
    #define AWI_REMOVE_DATA_ON_ERROR    true

    //Not so awesome error codes...
    enum AWESOME_ERROR_CODES
    {
        AWESOME_ERROR_NONE = 0, /*Everything is fine!*/
        AWESOME_ERROR_FILE_PATH, /*The INI specified has an invalid path!*/
        AWESOME_ERROR_MEMORY, /*Rare error code if something happens to break memory limits.*/
        AWESOME_ERROR_PARSING_INVALID_SEPERATOR_POS, /*The seperator is ether in the wrong location of the line or does not exsist!*/
        AWESOME_ERROR_PARSING_INVALID_VALUE_TYPE, /*The key value type is not reconized.*/
        AWESOME_ERROR_PARSING_LINE_TOO_SHORT, /*The line of the file is too short to read. Usually because of invalid syntax.*/
        AWESOME_ERROR_PARSING_INVALID_NAME, /*The key or group name has unsupported characters*/
        AWESOME_ERROR_PARSING_INVALID_LINE, /*The line entered usually means another syntax error.*/
        AWESOME_ERROR_DUPLICATED_KEY, /*The key entered has already been parsed in the same group.*/
        AWESOME_ERROR_DUBLICATED_GROUP_NAME, /*The group name has already been entered.*/
        AWESOME_ERROR_GROUP_NOT_FOUND, /*Usually called when the INI file no longer has a exsisting group.*/
        AWESOME_ERROR_KEY_NOT_FOUND,    /*The key entered is no longer in the correct group.*/
        AWESOME_ERROR_NO_GROUP_DEFINED, /*The group was not entered at the top of the INI file.*/
        AWESOME_ERROR_INVALID_DATA_TYPE_RETRIVED /*The data type attempted to be retrived is not the correct type.*/
    };

    enum AWESOME_INI_KEY_TYPE
    {
        AWESOME_TYPE_VOID = 0,
        AWESOME_TYPE_STRING,
        AWESOME_TYPE_INT,
        AWESOME_TYPE_FLOAT,
        AWESOME_TYPE_BOOL
    };


    struct awesome_key_element
    {
        std::string m_key;
        std::string m_value;
        AWESOME_INI_KEY_TYPE key_type;
    };

    struct awesome_key_groups
    {
        std::string m_name;
        std::vector<awesome_key_element> m_elements;
    };


    class awesome_ini
    {
    public:
        awesome_ini(std::string file);
        virtual ~awesome_ini(void);


        AWESOME_ERROR_CODES        getError(void);
        size_t                    getErrorLine(void);

        //Simple reads
        bool    getKeyType(AWESOME_INI_KEY_TYPE &type, std::string group, std::string key);
        bool    getKeyType(AWESOME_INI_KEY_TYPE &type, std::size_t group, std::string key);

        bool                   getKeyInt(int &p, std::string group, std::string key);
        bool                   getKeyInt(int &p, std::size_t group, std::string key);

        bool                   getKeyBool(bool &p, std::string group, std::string key);
        bool                   getKeyBool(bool &p, std::size_t group, std::string key);

        bool                   getKeyString(std::string &p, std::string group, std::string key);
        bool                   getKeyString(std::string &p, std::size_t group, std::string key);

        bool                   getKeyFloat(float &p, std::string group, std::string key);
        bool                   getKeyFloat(float &p, std::size_t group, std::string key);

        std::size_t            findGroup(std::string name);

        void                   clearAll(void);
    protected:
        //Vector

        bool                    findDuplicateGroupName(std::string name);
        bool                    findDuplicateKeyName(size_t group, std::string name);

        AWESOME_INI_KEY_TYPE    figureType(std::string val);

        AWESOME_INI_KEY_TYPE    getDataType(const char* line, size_t length);

        std::string                trim(std::string line); /*Trims useless white spacese...*/
        bool                    isNameValid(std::string name);
        std::string                aw_strToLower(std::string line);

        inline bool                throwError(AWESOME_ERROR_CODES error);
    private:


        AWESOME_ERROR_CODES        m_error;
        size_t                    m_error_line;

        std::vector<awesome_key_groups> m_list;
    };

    #endif

awesome_ini.cpp

#include "awesome_ini.h"

/*
AUTHOR: Andrew McRobb

DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
Version 2, December 2004

Copyright (C) 2015 Andrew McRobb <andrewmcrobb@gmail.com>

Everyone is permitted to copy and distribute verbatim or modified
copies of this license document, and changing it is allowed as long
as the name is changed.

DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

0. You just DO WHAT THE FUCK YOU WANT TO.

*/

awesome_ini::awesome_ini(std::string filename)
{
    m_error = AWESOME_ERROR_NONE;
    m_error_line = 0;
    std::ifstream myfile (filename);
    if (!myfile.is_open())
    {
        m_error = AWESOME_ERROR_FILE_PATH;
        return;
    }

    std::string line;
    size_t lineNumber = 1;
    size_t curGroup = 0;
    while ( getline (myfile,line) )
    {
        line = trim(line);

        if(line.size() < 1){
            continue;
        }

        /* IS IT A NEW GROUP? */
        if(line[0] == '['){

            std::size_t endi = line.find_last_of(']');

            if(endi == std::string::npos){
                m_error = AWESOME_ERROR_PARSING_INVALID_LINE;
                m_error_line = lineNumber;

                if(AWI_REMOVE_DATA_ON_ERROR){
                    clearAll();
                    return;
                }

                lineNumber++;
                continue;
            }

            std::string cat = aw_strToLower(line.substr(1, (endi-1)));

            if(findDuplicateGroupName(cat)){
                m_error = AWESOME_ERROR_DUBLICATED_GROUP_NAME;
                m_error_line = lineNumber;

                if(AWI_REMOVE_DATA_ON_ERROR){
                    clearAll();
                    return;
                }

                lineNumber++;
                continue;
            }

            awesome_key_groups newGroup;
            newGroup.m_name = cat;

            m_list.push_back(newGroup);
            curGroup++;
            lineNumber++;
            continue;
        }

        std::size_t deli = line.find(AWI_SEPERATOR_CHAR);

        if(deli == std::string::npos){

            m_error_line = lineNumber;
            m_error = AWESOME_ERROR_PARSING_INVALID_SEPERATOR_POS;

            if(AWI_REMOVE_DATA_ON_ERROR){
                clearAll();
                return;
            }

            lineNumber++;
            continue;

        }

        if(curGroup == 0){
            m_error_line = lineNumber;
            m_error = AWESOME_ERROR_NO_GROUP_DEFINED;
            break;
        }


        //Get the key and value...
        std::string key;
        std::string val;

        key = aw_strToLower(line.substr(0,deli));
        val = line.substr(deli+1);


        /* CHECK THE KEY IS VALID */
        if(!isNameValid(key)){
            m_error_line = lineNumber;
            m_error = AWESOME_ERROR_PARSING_INVALID_NAME;

            if(AWI_REMOVE_DATA_ON_ERROR){
                clearAll();
                return;
            }

            lineNumber++;
            continue;
        }

        /* ENSURE THE NAME IS ALREADY TAKEN! */
        if(findDuplicateKeyName((curGroup-1), key))
        {
            m_error_line = lineNumber;
            m_error = AWESOME_ERROR_DUPLICATED_KEY;

            if(AWI_REMOVE_DATA_ON_ERROR){
                clearAll();
                return;
            }

            lineNumber++;
            continue;
        }


        awesome_key_element  newKey;
        newKey.m_key = aw_strToLower(key);
        newKey.m_value = val;
        newKey.key_type = figureType(val);

        m_list.at((curGroup-1)).m_elements.push_back(newKey);

        lineNumber++;
    }

    myfile.close();
}

AWESOME_INI_KEY_TYPE awesome_ini::figureType(std::string val){

    //Is number?
    size_t string_length = val.length();

    val = aw_strToLower(val);

    /* IS IT A BOOL? */
    if(string_length <= 5)
    {
        if(val.compare("true") == 0 || val.compare("false") == 0)
        {
            return AWESOME_TYPE_BOOL;
        }
    }

    //Is it a stirng?
    for(size_t i = 0; i<string_length; i++)
    {
        char c = val.at(i);
        if(isalpha(c) && (c < 48 || c > 57))
        {
            return AWESOME_TYPE_STRING;
        }
    }

    bool isInt = true;
    for(size_t i = 0; i<string_length; i++)
    {
        char c = val.at(i);

        if(c == '\0'){
            break;
        }

        if(c < 48|| c > 57)
        {
            isInt = false;
            break;
        }
    }

    if(isInt){
        return AWESOME_TYPE_INT;
    }

    bool isFloat = true;
    for(size_t i = 0; i<string_length; i++)
    {

        char c = val.at(i);

        if(c == '\0'){
            break;
        }

        if((c < 48|| c > 57) && c != 46)
        {
            isFloat = false;
            break;
        }
    }

    if(isFloat){
        return AWESOME_TYPE_FLOAT;
    }

    return AWESOME_TYPE_VOID;
}


bool awesome_ini::throwError(AWESOME_ERROR_CODES error)
{
    m_error = error;
    bool quit = false;
    if (AWI_REMOVE_DATA_ON_ERROR)
    {
        clearAll();
        quit = true;
    }

    return quit;
}


//Does the key include characters not allowed?
bool awesome_ini::isNameValid(std::string line)
{
    size_t length = line.size();
    for (size_t i = 0; i < length; i++)
    {
        unsigned char c = line[i];
        if (!isalpha(c) && c != '_')
        {
            return false;
        }
    }

    return true;
}


awesome_ini::~awesome_ini()
{
    clearAll();
}

std::string awesome_ini::trim(std::string line)
{
    size_t length = line.size();

    if(length == 0)
    {
        return "";
    }

    //Look for comments and strip them!

    std::size_t comindex = line.find(AWI_COMMENT_CHAR);

    if(comindex != std::string::npos){
        line = line.substr(0, comindex);

        if(line.size() == 0){
            return "";
        }
    }

    bool ignoreSpaces = false;
    std::string destBuffer = "";

    for (size_t i = 0; i < length; i++)
    {
        char c = line[i];

        if(c == AWI_STRING_CHAR && !ignoreSpaces){
            ignoreSpaces = true;
            continue;
        }

        if(c == AWI_STRING_CHAR && ignoreSpaces){
            if(i > 1){
                if(line[i-1] == '\\'){
                    destBuffer += c;
                    continue;
                }
            }
            ignoreSpaces = false;
            continue;
        }

        if ((c == '\n' || c == ' ' || c == '\r' || c == '\t') && !ignoreSpaces)
        {
            continue;
        }



        destBuffer += c;
    }

    return destBuffer;
}

std::string awesome_ini::aw_strToLower(std::string line)
{
     size_t length = line.size();

    if(length == 0)
    {
        return "";
    }

    std::string dstBuffer;

    for (size_t i = 0; i < length; i++)
    {
        if(line[i]<='Z' && line[i]>='A')
        {
            char c = line[i];
            dstBuffer += c-('Z'-'z');
            continue;
        }

        dstBuffer += line[i];
    }

    return dstBuffer;
}

AWESOME_ERROR_CODES awesome_ini::getError(void)
{
    return m_error;
}

size_t awesome_ini::getErrorLine(void)
{
    return m_error_line;
}

std::size_t   awesome_ini::findGroup(std::string name)
{
    size_t group_size = m_list.size();

    if(group_size == 0){
        return std::string::npos;
    }

    for(size_t i = 0; i<group_size; i++)
    {
        if(m_list.at(i).m_name.compare(name) == 0)
        {
            return i;
        }
    }

    return std::string::npos;
}

bool awesome_ini::findDuplicateGroupName(std::string name)
{
    if(findGroup(name) != std::string::npos)
    {
        return true;
    }

    return false;
}

bool awesome_ini::findDuplicateKeyName(size_t group, std::string name)
{
    size_t group_size = m_list.size();

    if(group >= group_size){
        return false;
    }

    size_t group_key_size = m_list.at(group).m_elements.size();

    if(group_key_size == 0) {
        return false;
    }

    for(size_t i = 0; i<group_key_size; i++)
    {
        if(m_list.at(group).m_elements.at(i).m_key.compare(name) == 0){
            return true;
        }
    }

    return false;
}


//delete all data!
void awesome_ini::clearAll(void)
{
    size_t group_size = m_list.size();

    for(size_t i = 0; i<group_size; i++)
    {
        m_list.at(i).m_elements.clear();
    }

    m_list.clear();
}

//Gets key value. Returns data type void if nothing found and error.
bool awesome_ini::getKeyType(AWESOME_INI_KEY_TYPE &type, std::string group, std::string key)
{
    std::size_t group_index = findGroup(group);

    if(group_index == std::string::npos){
        m_error = AWESOME_ERROR_GROUP_NOT_FOUND;
        return false;
    }

    size_t key_size = m_list.at(group_index).m_elements.size();

    if(key_size == 0){
        return false;
    }

    for(size_t i = 0; i<key_size; i++){
        if(m_list.at(group_index).m_elements.at(i).m_key.compare(key) == 0){
            type = m_list.at(group_index).m_elements.at(i).key_type;
            return true;
        }
    }

    return false;
}

//Gets key value. Returns data type void if nothing found and error.
bool awesome_ini::getKeyType(AWESOME_INI_KEY_TYPE &type, size_t group, std::string key)
{
    std::size_t group_size = m_list.size();
    if(group >= group_size){
        m_error = AWESOME_ERROR_GROUP_NOT_FOUND;
        return false;
    }

    size_t key_size = m_list.at(group).m_elements.size();

    if(key_size == 0){
        return false;
    }

    for(size_t i = 0; i<key_size; i++){
        if(m_list.at(group).m_elements.at(i).m_key.compare(key) == 0){
            type = m_list.at(group).m_elements.at(i).key_type;
            return true;
        }
    }

    return false;
}




bool awesome_ini::getKeyInt(int &p, std::string group, std::string key)
{
    std::size_t group_index = findGroup(group);

    if(group_index == std::string::npos){
        m_error = AWESOME_ERROR_GROUP_NOT_FOUND;
        return false;
    }


    size_t key_size = m_list.at(group_index).m_elements.size();

    if(key_size == 0){
        return false;
    }

    for(size_t i = 0; i<key_size; i++){
        if(m_list.at(group_index).m_elements.at(i).m_key.compare(key) == 0){

            if(m_list.at(group_index).m_elements.at(i).key_type != AWESOME_TYPE_INT) {
                m_error = AWESOME_ERROR_INVALID_DATA_TYPE_RETRIVED;
                return false;
            }

            p = atoi(m_list.at(group_index).m_elements.at(i).m_value.c_str());
            return true;
        }
    }

    m_error = AWESOME_ERROR_KEY_NOT_FOUND;
    return false;
}

bool awesome_ini::getKeyInt(int &p, size_t group, std::string key)
{
    std::size_t group_size = m_list.size();
    if(group >= group_size){
        m_error = AWESOME_ERROR_GROUP_NOT_FOUND;
        return false;
    }

    size_t key_size = m_list.at(group).m_elements.size();

    if(key_size == 0){
        return false;
    }

    for(size_t i = 0; i<key_size; i++){
        if(m_list.at(group).m_elements.at(i).m_key.compare(key) == 0){

            if(m_list.at(group).m_elements.at(i).key_type != AWESOME_TYPE_INT) {
                m_error = AWESOME_ERROR_INVALID_DATA_TYPE_RETRIVED;
                return false;
            }

            p = atoi(m_list.at(group).m_elements.at(i).m_value.c_str());
            return true;
        }
    }

    m_error = AWESOME_ERROR_KEY_NOT_FOUND;
    return false;
}


bool awesome_ini::getKeyBool(bool &p, std::string group, std::string key)
{
    std::size_t group_index = findGroup(group);

    if(group_index == std::string::npos){
        m_error = AWESOME_ERROR_GROUP_NOT_FOUND;
        return false;
    }

    size_t key_size = m_list.at(group_index).m_elements.size();

    if(key_size == 0){
        return false;
    }

    std::string buffer;

    for(size_t i = 0; i<key_size; i++){
        if(m_list.at(group_index).m_elements.at(i).m_key.compare(key) == 0){

            if(m_list.at(group_index).m_elements.at(i).key_type != AWESOME_TYPE_BOOL) {
                m_error = AWESOME_ERROR_INVALID_DATA_TYPE_RETRIVED;
                return false;
            }

           buffer = m_list.at(group_index).m_elements.at(i).m_value;
        }
    }

    if(buffer.size() == 0){
        m_error = AWESOME_ERROR_KEY_NOT_FOUND;
        return false;
    }

    //Convert string to lowercase...
    buffer = aw_strToLower(buffer);

    p = false;
    if (buffer.compare("true") == 0)
    {
        p = true;
    }

    return true;
}

bool awesome_ini::getKeyBool(bool &p, size_t group, std::string key)
{

    std::size_t group_size = m_list.size();
    if(group >= group_size){
        m_error = AWESOME_ERROR_GROUP_NOT_FOUND;
        return false;
    }

    size_t key_size = m_list.at(group).m_elements.size();

    if(key_size == 0){
        return false;
    }

    std::string buffer;

    for(size_t i = 0; i<key_size; i++){
        if(m_list.at(group).m_elements.at(i).m_key.compare(key) == 0){
            if(m_list.at(group).m_elements.at(i).key_type != AWESOME_TYPE_BOOL) {
                m_error = AWESOME_ERROR_INVALID_DATA_TYPE_RETRIVED;
                return false;
            }
            buffer = m_list.at(group).m_elements.at(i).m_value;
        }
    }

    if(buffer.size() == 0){
        m_error = AWESOME_ERROR_KEY_NOT_FOUND;
        return false;
    }

    //Convert string to lowercase...
    buffer = aw_strToLower(buffer);

    p = false;
    if (buffer.compare("true") == 0)
    {
        p = true;
    }

    return true;
}



bool awesome_ini::getKeyFloat(float &p, std::string group, std::string key)
{
    std::size_t group_index = findGroup(group);

    if(group_index == std::string::npos){
        m_error = AWESOME_ERROR_GROUP_NOT_FOUND;
        return false;
    }

    size_t key_size = m_list.at(group_index).m_elements.size();

    if(key_size == 0){
        return false;
    }

    for(size_t i = 0; i<key_size; i++){
        if(m_list.at(group_index).m_elements.at(i).m_key.compare(key) == 0){
            if(m_list.at(group_index).m_elements.at(i).key_type != AWESOME_TYPE_FLOAT) {
                m_error = AWESOME_ERROR_INVALID_DATA_TYPE_RETRIVED;
                return false;
            }
            p = atof(m_list.at(group_index).m_elements.at(i).m_value.c_str());
            return true;
        }
    }

    m_error = AWESOME_ERROR_KEY_NOT_FOUND;
    return false;
}

bool awesome_ini::getKeyFloat(float &p, size_t group, std::string key)
{
    std::size_t group_size = m_list.size();
    if(group >= group_size){
        m_error = AWESOME_ERROR_GROUP_NOT_FOUND;
        return false;
    }

    size_t key_size = m_list.at(group).m_elements.size();

    if(key_size == 0){
        return false;
    }

    for(size_t i = 0; i<key_size; i++){
        if(m_list.at(group).m_elements.at(i).m_key.compare(key) == 0){
            if(m_list.at(group).m_elements.at(i).key_type != AWESOME_TYPE_FLOAT) {
                m_error = AWESOME_ERROR_INVALID_DATA_TYPE_RETRIVED;
                return false;
            }
            p = atof(m_list.at(group).m_elements.at(i).m_value.c_str());
            return true;
        }
    }

    m_error = AWESOME_ERROR_KEY_NOT_FOUND;
    return false;
}

bool awesome_ini::getKeyString(std::string &p, std::string group, std::string key)
{
    std::size_t group_index = findGroup(group);

    if(group_index == std::string::npos){
        m_error = AWESOME_ERROR_GROUP_NOT_FOUND;
        return false;
    }

    size_t key_size = m_list.at(group_index).m_elements.size();

    if(key_size == 0){
        return false;
    }

    for(size_t i = 0; i<key_size; i++){
        if(m_list.at(group_index).m_elements.at(i).m_key.compare(key) == 0){
            if(m_list.at(group_index).m_elements.at(i).key_type != AWESOME_TYPE_STRING) {
                m_error = AWESOME_ERROR_INVALID_DATA_TYPE_RETRIVED;
                return false;
            }
            p = m_list.at(group_index).m_elements.at(i).m_value.c_str();
            return true;
        }
    }

    m_error = AWESOME_ERROR_KEY_NOT_FOUND;
    return false;
}

bool awesome_ini::getKeyString(std::string &p, size_t group, std::string key)
{
    std::size_t group_size = m_list.size();
    if(group >= group_size){
        m_error = AWESOME_ERROR_GROUP_NOT_FOUND;
        return false;
    }

    size_t key_size = m_list.at(group).m_elements.size();

    if(key_size == 0){
        return false;
    }

    for(size_t i = 0; i<key_size; i++){
        if(m_list.at(group).m_elements.at(i).m_key.compare(key) == 0){
            if(m_list.at(group).m_elements.at(i).key_type != AWESOME_TYPE_STRING) {
                m_error = AWESOME_ERROR_INVALID_DATA_TYPE_RETRIVED;
                return false;
            }
            p = m_list.at(group).m_elements.at(i).m_value.c_str();
            return true;
        }
    }

    m_error = AWESOME_ERROR_KEY_NOT_FOUND;

    return false;
}

Example:

#include "awesome_ini_loader/awesome_ini.h"

int main(int argc, const char * argv[]) {
    // insert code here...
    awesome_ini parser("config.ini");

    std::cout << "Error Code: " << parser.getError() << " Error Line:" << parser.getErrorLine() << "\n";

    if(parser.getError() > 0)
    {
        std::cout << "Quit on error!";
        return EXIT_FAILURE;
    }

    //Get group ID.
    size_t gid = parser.findGroup("config");

    int val = 0;
    if(parser.getKeyInt(val, gid, "test"))
    {
        std::cout << "Int Val: " << val << "\n";
    }

    std::string sVal = "";
    if(parser.getKeyString(sVal, gid, "string"))
    {
        std::cout << "String Val: " << sVal.c_str() << "\n";
    }

    bool bVal = false;
    if(parser.getKeyBool(bVal, gid, "boolean"))
    {
        if(bVal){
            std::cout << "Bool Val: TRUE\n";
        }else{
            std::cout << "Bool Val: FALSE\n";
        }
    }

    float fVal = 0;
    if(parser.getKeyFloat(fVal, gid, "floatnumber"))
    {
        std::cout << "Float Val: " << fVal << "\n";
    }

    parser.clearAll();

    std::cout << "Done.\n";
    return EXIT_SUCCESS;
}
\$\endgroup\$
  • 1
    \$\begingroup\$ Please take a look at our help-center. \$\endgroup\$ – jliv902 Mar 13 '15 at 16:49
6
\$\begingroup\$

Here are some points to start with, mostly related to coding style and overall C++ practices (roughly in order of appearance):

  1. Careful with your comments, some people might find things like this offensive or inappropriate:

    DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
    

    That could drive users away from your project and I would personally never send code with this sort of comments as a portfolio for a job position either.

    If you just wish to wave copyrights over the code, this should suffice:

    // This code is public domain. I claim no copyrights over it.
    // No warranty is offered or implied; use it at your own risk.
    

    And if you want to get creative, just add a quote from your favorite philosopher at the end.

    Edit note: So it turns out that WTFPL is an actual license... Not sure what to make of this... Thanks @FelixBytow for pointing it out.

  2. You have nested everything in the header file under the #ifndef AWESOME_INI_H include guard directive. Don't add that level of indentation. It is not just very unusual, but it also adds nothing to the understanding of the code at the cost of extra horizontal spacing.

  3. Avoid #defines for constants. They have no type and provide bad error messages when you use them improperly. What you want for those string constants in the header file is a const char [] or const std::string. Those string constants should also be declared in the .cpp file only, since they are only relevant to the parser internals. Never expose implementation details to the public interface.

  4. Consider using the strongly typed enum class if you are targeting C++11.

  5. Writing void in the parameter list of a C++ function that takes no parameters (as in getError(void)) is a C-ish style. C++ does not require that; () is just as good. So avoid the unnecessary verbosity.

  6. virtual destructors are only necessary when your class is meant to be inherited from. In this particular code, that doesn't seem to be the case. If you did that to allow users of your code to inherit from awesome_ini, I would advise against, since Composition is usually much more elegant and with less coupling. You should remove virtual from the destructor declaration and perhaps even mark your class final.

  7. Consider using a namespace to nest your library, instead of prefixing everything with awesome_. A namespace awesome { } would be neater.

  8. You have way too many includes exposed in the header file. You don't need ctype.h, string.h and stdlib.h in there. Those should go in the implementation (.cpp). This also falls into the category of not exposing implementation details.

  9. By the way, the header files mentioned above are the C headers. In C++ you should be including <cctype>, <cstring> and <cstdlib>.

  10. In most places you are using std::size_t and in a few others just size_t. Make sure you replace those with std::size_t.

  11. The constructor of awesome_ini is massive. I suggest breaking it into a few more specific methods to achieve Single Responsibility.

  12. Don't use decimal values for character literals (e.g.: c < 48 || c > 57 and other places). Use the proper ascii representation ('0' / '9'). std::isdigit() also comes to mind...

  13. Which brings use to the next one. Avoid reinventing the wheel. This:

    (c == '\n' || c == ' ' || c == '\r' || c == '\t')
    

    Is done by the std::isspace() function from <cctype>.

  14. You have a few methods that take a std::string as parameter and only read from the string (without making copies of it). In such cases, it is better to just take the object by const reference (const std::string & str). Makes it clear that the function only look at the object without copying it. Of course that this is valid for User Defined Types only. Native types such as ints and floats should always be passed by value.

  15. The atoi() function is deprecated and has quite a few issues. Prefer std::stoi() (C++11) or std::strtol() otherwise.

  16. Since you've named all of your types with snake_case I think it would be nice to keep consistency and also name the methods using this notation. The other way around would also work. PascalCase for types and camelCase for methods and variables.

| improve this answer | |
\$\endgroup\$
  • 1
    \$\begingroup\$ There are two points I do not approve of: 1. WTFPL is an actual license. 2. This is called an include guard, very usual and actually best practice and even needed in most cases. Maybe you though of #pragma once instead, but that is actually non standard and may not be supported by every compiler. The rest is fine, I enjoyed reading it. \$\endgroup\$ – Felix Bytow Mar 13 '15 at 18:36
  • 1
    \$\begingroup\$ @FelixBytow, haha, I was not aware of that license... Not sure what to say now... And I'm not talking about the include guard, but about nesting the code under it, which is a waste of horizontal space IMHO. That shall be clarified, thanks. \$\endgroup\$ – glampert Mar 13 '15 at 18:39
  • 1
    \$\begingroup\$ Now I get you. Yes, indenting everything inside the include guards is indeed unusual. \$\endgroup\$ – Felix Bytow Mar 13 '15 at 18:42
  • 1
    \$\begingroup\$ Wow, thank you. I defiantly cleaned up my code. Added namespaces, removed deprecated functions, stopped reinventing the wheel, even found a memory error while working on it, changed the license from WTFPL to MIT (Something more realistic), removed the voids in 0 argument functions, replaced defines with const variables, removed unused headers. I'll clean up my function names too to keep consistency. \$\endgroup\$ – ajm113 Mar 13 '15 at 20:37
  • 1
    \$\begingroup\$ @ajm113 defiantly or definitely? :) \$\endgroup\$ – user253751 Mar 13 '15 at 21:12

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.