Solved

Help understanding scope issue

Posted on 2014-10-14
9
188 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
  • 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
Free Tool: ZipGrep

ZipGrep is a utility that can list and search zip (.war, .ear, .jar, etc) archives for text patterns, without the need to extract the archive's contents.

One of a set of tools we're offering as a way to say thank you for being a part of the community.

 
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

Active Directory Webinar

We all know we need to protect and secure our privileges, but where to start? Join Experts Exchange and ManageEngine on Tuesday, April 11, 2017 10:00 AM PDT to learn how to track and secure privileged users in Active Directory.

Question has a verified solution.

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

Suggested Solutions

When writing generic code, using template meta-programming techniques, it is sometimes useful to know if a type is convertible to another type. A good example of when this might be is if you are writing diagnostic instrumentation for code to generat…
Written by John Humphreys C++ Threading and the POSIX Library This article will cover the basic information that you need to know in order to make use of the POSIX threading library available for C and C++ on UNIX and most Linux systems.   [s…
The viewer will be introduced to the technique of using vectors in C++. The video will cover how to define a vector, store values in the vector and retrieve data from the values stored in the vector.
The viewer will be introduced to the member functions push_back and pop_back of the vector class. The video will teach the difference between the two as well as how to use each one along with its functionality.

831 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