Home > Enterprise >  Seperating C Nested Classes into their Own Header Files
Seperating C Nested Classes into their Own Header Files

Time:11-13

new to this site and also C but hoping to see some guidance from everyone.

I had a pretty fun project idea to learn C digging deeper with APIs, classes, references, etc. and currently I have a working example of code where everything exist within the main.cpp file. The issue I am facing is that when i move the classes (inner and outer) to their own respective header files the code no longer compiles.

The reason for the nested classes is that the OuterAPI serves as the main entry point to the API and has many lower level APIs that can be then accessed beneath it (people, licenes, roles, etc). This way users of API would only have to create an object for the OuterAPI and then dot notation for underlying resource and method.

Here is the working example in the main.cpp

#include <iostream>
#include <nlohmann/json.hpp>
#include <cpr/cpr.h>
using json = nlohmann::json;

class OuterAPI {
private:
    class InnerAPI {
    private:
        OuterAPI& api;
    public:
        InnerAPI(OuterAPI& a) :api(a) {}
        json get() {
            cpr::Response r = cpr::Get(
                cpr::Url{ api.baseUrl   "resource" },
                cpr::Bearer{ api.token }
            );
            return json::parse(r.text)
    };

    std::string token;
    std::string baseUrl = "";
public:
    InnerAPI people;
    OuterAPI(std::string t) : token(t), people(*this) {}
};

int main(int argc, char** argv)
{
    std::string token = "";
    OuterAPI api(token);
    json jsonData = api.people.get();
    std::cout << jsonData.dump(4) << std::endl;

    return 0;
}

Here is me moving everything to respective header/cpp files

OuterAPI.h

#pragma once

class OuterAPI {
private:
    class InnerAPI;
    std::string token;
    std::string baseUrl = "";
public:
    OuterAPI(std::string t);
    ~OuterAPI();
    InnerAPI* people;
};

OuterAPI.cpp

#include "WebexAPI.h"
#include "PeopleAPI.h"

OuterAPI::OuterAPI(std::string t) : token(t) {
    people = new InnerAPI(*this);
}

OuterAPI::~OuterAPI() { delete people; }

InnerAPI.h

#pragma once

#include <nlohmann/json.hpp>
#include <cpr/cpr.h>
#include "OuterAPI.h"

using json = nlohmann::json;

class OuterAPI::InnerAPI {
private:
    OuterAPI& api;
public:
    InnerAPI(OuterAPI& a);
    json get();
};

InnerAPI.cpp

#include "InnerAPI.h"

OuterAPI::InnerAPI::InnerAPI(OuterAPI& a) : api(a) {}

json OuterAPI::InnerAPI::get() {
    cpr::Response r = cpr::Get(
        cpr::Url{ api.baseUrl   "resource" },
        cpr::Bearer{ api.token }
    );
    return json::parse(r.text);

main.cpp (finally) - this is where the compiler error occurs at api.people.get() "expression must have class type but has type "OuterAPI::InnerAPI *"

int main(int argc, char** argv)
{
    std::string token = "";
    OuterAPI api(token);
    json jsonData = api.people.get();     // COMPILER ERROR "expression must have class type but has type "OuterAPI::InnerAPI *"
    std::cout << jsonData.dump(4) << std::endl;

    return 0;
}

From this I believe the issue is associated with me having to define the InnerAPI object people as a pointer inside of OuterAPI but from here I cant seem to come to a resolution.

Also, feel free to critique my design as well, like I say I am new to C so want to make sure I can do a good job. Thanks.

CodePudding user response:

In OuterAPI* you have declared people as a member of type InnerAPI*.

You can either call your API using api.people->get() or make the member a InnerAPI instead.

EDIT:

It seems the error, besides the pointer thing, comes from how you handle file includes. I managed to get a working version on REPL.it. I made slight adjustments so I wouldn't have to bring both libraries in so focus on the gist of it. Here it is:

OuterAPI.h

#pragma once

#include <string>

class OuterAPI {
private:
    class InnerAPI;
    std::string token;
    std::string baseUrl = "";
public:
    OuterAPI(std::string t);
    ~OuterAPI();
    InnerAPI* people;
};

InnerAPI.j

#pragma once

#include "./OuterAPI.h"

class OuterAPI::InnerAPI {
private:
    OuterAPI& api;
public:
    InnerAPI(OuterAPI& a);
    std::string get();
};

OuterAPI.cpp

#include "./OuterAPI.h"
#include "./InnerAPI.h"

OuterAPI::OuterAPI(std::string t) : token(t) {
    people = new InnerAPI(*this);
}

OuterAPI::~OuterAPI() { delete people; }

InnerAPI.cpp

#include "./OuterAPI.h"
#include "./InnerAPI.h"

OuterAPI::InnerAPI::InnerAPI(OuterAPI& a) : api(a) {}

std::string OuterAPI::InnerAPI::get() {
    return api.baseUrl   "resource";
}

CodePudding user response:

Make sure you include everything you intend to use in every file where you intend to do so.
Separating declaration and definition is pretty common.
It's a way to reduce compile time on large projects.
Thankfully modules will soon™ make linking a thing of the past.

To address the error message: you declare people as a raw member pointer of the class OuterAPI… You cannot access a member through a pointer using operator ., you need to use operator ->.

  • Related