Solved

Help understanding scope issue

Posted on 2014-10-14
9
196 Views
Last Modified: 2014-10-14
If have this bit of code that invokes a web server and works fine:

void  main(){
    const char * options[] = { "document_root", DOCUMENT_ROOT,
        "listening_ports", PORT, 0
    };
    CivetServer server(options);
    while (!exitNow) {
      Sleep(1000);
    }
}

However, I'd like to move the part that starts the "Civetserver" out into another function, just to separate it out, like this:

void main(){
    initialize();
    while (!exitNow) {
      Sleep(1000);
    }
}
void initialize(){
    const char * options[] = { "document_root", DOCUMENT_ROOT,
        "listening_ports", PORT, 0
    };
    CivetServer server(options);
}

This works while we are inside the function "initialize". But it appears that whenever I leave the "initialize" function, the var "server" is gone, I guess destroyed because it's not in scope?

Trying to get around this, I also tried this, and it had the same result, but I'm not sure why.
void main(){
    CivetServer server = initialize();
    while (!exitNow) {
      Sleep(1000);
    }
}
CivetServer initialize(){
    const char * options[] = { "document_root", DOCUMENT_ROOT,
        "listening_ports", PORT, 0
    };
    CivetServer server(options);
    return server;
}

I'm a newb and probably just have some fundamental misunderstanding of what I'm doing here.
0
Comment
Question by:rustycp
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 4
  • 3
  • 2
9 Comments
 
LVL 31

Accepted Solution

by:
Zoppo earned 300 total points
ID: 40380636
Hi rustycp,

this depends on how the CivetServer is implemented. If it's a class which can safely be copied using assignment operator or copy constructor your second approach can work. The first approach can't really work since you use server as a function-local variable which is destroyed when it falls out from scope when the initialize function exits.

In case it's not possible to use your second approach (i.o.w. if the object can't be copied safely and if there's no other way to create a complete instance than calling the constructor) easiest is to use a pointer, i.e.
void main(){
    CivetServer* pServer = initialize();
    while (!exitNow) {
      Sleep(1000);
    }
   delete pServer; // clean up
}
CivetServer initialize(){
    const char * options[] = { "document_root", DOCUMENT_ROOT,
        "listening_ports", PORT, 0
    };
    return new CivetServer(options);
}

Open in new window

Hope this helps,

ZOPPO
0
 
LVL 86

Assisted Solution

by:jkr
jkr earned 200 total points
ID: 40380646
Your 'CivetServer' instance is a local variable to 'initialize()', so it will be destroyed as soon as the function exits (see e.g. http://www.tutorialspoint.com/cplusplus/cpp_variable_scope.htm). You are quite close in your 2nd example, but if the class does not have a copy constructor and assignment operator, you might not be able to return it from your function.

The basic ide here would be to use a 'CivetServer*' and create the instance dynamically like

void main(){
    CivetServer* server = initialize();
    while (!exitNow) {
      Sleep(1000);
    }
   terminate(server);
}
CivetServer* initialize(){
    const char * options[] = { "document_root", DOCUMENT_ROOT,
        "listening_ports", PORT, 0
    };
    CivetServer* server = new CivetServer(options);
    return server;
}
void terminate(CivetServer* server) {

  delete server;
}

Open in new window

0
 
LVL 2

Author Comment

by:rustycp
ID: 40380665
Hm I see the direction depends on the class... here is the exact class. The docs tell me that this actually starts the server:

CivetServer server(options);



/**
 * CivetServer
 *
 * Basic class for embedded web server.  This has an URL mapping built-in.
 */
class CivetServer
{
public:

    /**
     * Constructor
     *
     * This automatically starts the sever.
     * It is good practice to call getContext() after this in case there
     * were errors starting the server.
     *
     * @param options - the web server options.
     * @param callbacks - optional web server callback methods.
     */
    CivetServer(const char **options, const struct mg_callbacks *callbacks = 0);

    /**
     * Destructor
     */
    virtual ~CivetServer();

    /**
     * close()
     *
     * Stops server and frees resources.
     */
    void close();

    /**
     * getContext()
     *
     * @return the context or 0 if not running.
     */
    const struct mg_context *getContext() const {
        return context;
    }

    /**
     * addHandler(const std::string &, CivetHandler *)
     *
     * Adds a URI handler.  If there is existing URI handler, it will
     * be replaced with this one.
     *
     * URI's are ordered and prefix (REST) URI's are supported.
     *
     *  @param uri - URI to match.
     *  @param handler - handler instance to use.  This will be free'ed
     *      when the server closes and instances cannot be reused.
     */
    void addHandler(const std::string &uri, CivetHandler *handler);

    /**
     * removeHandler(const std::string &)
     *
     * Removes a handler.
     *
     * @param uri - the exact URL used in addHandler().
     */
    void removeHandler(const std::string &uri);

    /**
     * getCookie(struct mg_connection *conn, const std::string &cookieName, std::string &cookieValue)
     *
     * Puts the cookie value string that matches the cookie name in the cookieValue destinaton string.
     *
     * @param conn - the connection information
     * @param cookieName - cookie name to get the value from
     * @param cookieValue - cookie value is returned using thiis reference
     * @returns the size of the cookie value string read.
    */
    static int getCookie(struct mg_connection *conn, const std::string &cookieName, std::string &cookieValue);

    /**
     * getHeader(struct mg_connection *conn, const std::string &headerName)
     * @param conn - the connection information
     * @param headerName - header name to get the value from
     * @returns a char array whcih contains the header value as string
    */
    static const char* getHeader(struct mg_connection *conn, const std::string &headerName);

    /**
     * getParam(struct mg_connection *conn, const char *, std::string &, size_t)
     *
     * Returns a query paramter contained in the supplied buffer.  The
     * occurance value is a zero-based index of a particular key name.  This
     * should not be confused with the index over all of the keys.  Note that this
     * function assumes that parameters are sent as text in http query string
     * format, which is the default for web forms. This function will work for
     * html forms with method="GET" and method="POST" attributes. In other cases,
     * you may use a getParam version that directly takes the data instead of the
     * connection as a first argument.
     *
     * @param conn - parameters are read from the data sent through this connection
     * @param name - the key to search for
     * @param dst - the destination string
     * @param occurrence - the occurrence of the selected name in the query (0 based).
     * @return true if key was found
     */
    static bool getParam(struct mg_connection *conn, const char *name,
                         std::string &dst, size_t occurrence=0);

    /**
     * getParam(const std::string &, const char *, std::string &, size_t)
     *
     * Returns a query paramter contained in the supplied buffer.  The
     * occurance value is a zero-based index of a particular key name.  This
     * should not be confused with the index over all of the keys.
     *
     * @param data - the query string (text)
     * @param name - the key to search for
     * @param dst - the destination string
     * @param occurrence - the occurrence of the selected name in the query (0 based).
     * @return true if key was found
     */
    static bool getParam(const std::string &data, const char *name,
                         std::string &dst, size_t occurrence=0) {
        return getParam(data.c_str(), data.length(), name, dst, occurrence);
    }

    /**
     * getParam(const char *, size_t, const char *, std::string &, size_t)
     *
     * Returns a query paramter contained in the supplied buffer.  The
     * occurance value is a zero-based index of a particular key name.  This
     * should not be confused with the index over all of the keys.
     *
     * @param data the - query string (text)
     * @param data_len - length of the query string
     * @param name - the key to search for
     * @param dst - the destination string
     * @param occurrence - the occurrence of the selected name in the query (0 based).
     * @return true if key was found
     */
    static bool getParam(const char *data, size_t data_len, const char *name,
                         std::string &dst, size_t occurrence=0);


    /**
     * urlDecode(const std::string &, std::string &, bool)
     *
     * @param src - string to be decoded
     * @param dst - destination string
     * @param is_form_url_encoded - true if form url encoded
     *       form-url-encoded data differs from URI encoding in a way that it
     *       uses '+' as character for space, see RFC 1866 section 8.2.1
     *       http://ftp.ics.uci.edu/pub/ietf/html/rfc1866.txt
     */
    static void urlDecode(const std::string &src, std::string &dst, bool is_form_url_encoded=true) {
        urlDecode(src.c_str(), src.length(), dst, is_form_url_encoded);
    }

    /**
     * urlDecode(const char *, size_t, std::string &, bool)
     *
     * @param src - buffer to be decoded
     * @param src_len - length of buffer to be decoded
     * @param dst - destination string
     * @param is_form_url_encoded - true if form url encoded
     *       form-url-encoded data differs from URI encoding in a way that it
     *       uses '+' as character for space, see RFC 1866 section 8.2.1
     *       http://ftp.ics.uci.edu/pub/ietf/html/rfc1866.txt
     */
    static void urlDecode(const char *src, size_t src_len, std::string &dst, bool is_form_url_encoded=true);

    /**
     * urlDecode(const char *, std::string &, bool)
     *
     * @param src - buffer to be decoded (0 terminated)
     * @param dst - destination string
     * @param is_form_url_encoded true - if form url encoded
     *       form-url-encoded data differs from URI encoding in a way that it
     *       uses '+' as character for space, see RFC 1866 section 8.2.1
     *       http://ftp.ics.uci.edu/pub/ietf/html/rfc1866.txt
     */
    static void urlDecode(const char *src, std::string &dst, bool is_form_url_encoded=true);

    /**
     * urlEncode(const std::string &, std::string &, bool)
     *
     * @param src - buffer to be encoded
     * @param dst - destination string
     * @param append - true if string should not be cleared before encoding.
     */
    static void urlEncode(const std::string &src, std::string &dst, bool append=false) {
        urlEncode(src.c_str(), src.length(), dst, append);
    }

    /**
     * urlEncode(const char *, size_t, std::string &, bool)
     *
     * @param src - buffer to be encoded (0 terminated)
     * @param dst - destination string
     * @param append - true if string should not be cleared before encoding.
     */
    static void urlEncode(const char *src, std::string &dst, bool append=false);

    /**
     * urlEncode(const char *, size_t, std::string &, bool)
     *
     * @param src - buffer to be encoded
     * @param src_len - length of buffer to be decoded
     * @param dst - destination string
     * @param append - true if string should not be cleared before encoding.
     */
    static void urlEncode(const char *src, size_t src_len, std::string &dst, bool append=false);

protected:
    class CivetConnection {
    public:
        char * postData;
        unsigned long postDataLen;

        CivetConnection();
        ~CivetConnection();
    };

    struct mg_context *context;
    std::map<struct mg_connection *, class CivetConnection> connections;

private:
    /**
     * requestHandler(struct mg_connection *, void *cbdata)
     *
     * Handles the incomming request.
     *
     * @param conn - the connection information
     * @param cbdata - pointer to the CivetHandler instance.
     * @returns 0 if implemented, false otherwise
     */
    static int requestHandler(struct mg_connection *conn, void *cbdata);

    /**
     * closeHandler(struct mg_connection *)
     *
     * Handles closing a request (internal handler)
     *
     * @param conn - the connection information
     */
    static void closeHandler(struct mg_connection *conn);

    /**
     * Stores the user provided close handler
     */
    void (*userCloseHandler)(struct mg_connection *conn);

};
0
VIDEO: THE CONCERTO CLOUD FOR HEALTHCARE

Modern healthcare requires a modern cloud. View this brief video to understand how the Concerto Cloud for Healthcare can help your organization.

 
LVL 2

Author Comment

by:rustycp
ID: 40380672
I was originally thinking the class was a singleton, but then I wouldn't understand why it gets destroyed when I leave the initialize function. I guess the issue is that I don't understand how it is implemented, like you said.
0
 
LVL 31

Expert Comment

by:Zoppo
ID: 40380673
Well, there's no copy constructor and nor assignment operator, so since pointers are used in the class (struct mg_context *context;) and because it IMO even is very uncommon to have copyable objects which act as some kind of server I think you should use my or jkr's suggested code.

Best regards,

ZOPPO
0
 
LVL 86

Assisted Solution

by:jkr
jkr earned 200 total points
ID: 40380680
Yes, ist seems that thre is no way to copy the server as in your 1st approach. Funny enough, Zoppy and I came up with exactly the same idea to fix it :-D
0
 
LVL 2

Author Comment

by:rustycp
ID: 40380752
To make sure I'm understanding this, in the original code this line:

      CivetServer server(options);

"server" would be a CivetServer instance

and in the new code:

CivetServer* server = new CivetServer(options);

"server" would be a pointer to that CivetServer instance.

In my code, there are places that I called some functions on the "server" object using the dot notation. Like server.addHandler().  With this change, would I now change those calls to be server->addHandler(), since I am calling a function on a pointer to an object. Am I right?
0
 
LVL 31

Expert Comment

by:Zoppo
ID: 40380769
Yes, both is right, instead of an instance you use a pointer to an instance and therefor you have to use '->' as dereferencing operator instead of '.'
0
 
LVL 31

Expert Comment

by:Zoppo
ID: 40380785
BTW, if you really want to you could keep your other code mostly untouched using a reference (allthough it's possibly a bit dnageruous, see last comment in the code fragments below), i.e.:

CivetServer* pServer = new CivetServer(options);

// before calling next line you should check pServer is not NULL, otherwise next line would crash
CivetServer& server = *pServer;
...
server.addHandler(); // works the same as before.

When you want to call other functions which do something with this instance you can either pass it by pointer ot by reference, i.e.

void foo ( CivetServer* pServer )
{
 pServer->do_something();
}

void bar( CivetServer& server )
{
 server.do_something();
}

void foobar()
{
 CivetServer* pServer = new CivetServer(options);
 foo( pServer );

 CivetServer& server = *pServer;
 bar( server );

 delete pServer;
 // WARNING: Don't use 'server' after this again coz it will lead to runtime errors and most probably a crash.
}

ZOPPO
0

Featured Post

Industry Leaders: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

In days of old, returning something by value from a function in C++ was necessarily avoided because it would, invariably, involve one or even two copies of the object being created and potentially costly calls to a copy-constructor and destructor. A…
This article shows you how to optimize memory allocations in C++ using placement new. Applicable especially to usecases dealing with creation of large number of objects. A brief on problem: Lets take example problem for simplicity: - I have a G…
The viewer will learn additional member functions of the vector class. Specifically, the capacity and swap member functions will be introduced.
The viewer will learn how to clear a vector as well as how to detect empty vectors in C++.

630 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question