3

I am using a library which contains the following declaration in its header (http_client.h):

typedef struct _httpc_state httpc_state_t;

The library defines the struct in the implementation (http_client.c)

typedef struct _httpc_state
{
  struct altcp_pcb* pcb;
  ip_addr_t remote_addr;
  u16_t remote_port;
  int timeout_ticks;
  struct pbuf *request;
  struct pbuf *rx_hdrs;
  u16_t rx_http_version;
  u16_t rx_status;
  altcp_recv_fn recv_fn;
  const httpc_connection_t *conn_settings;
  void* callback_arg;
  u32_t rx_content_len;
  u32_t hdr_content_len;
  httpc_parse_state_t parse_state;
#if HTTPC_DEBUG_REQUEST
  char* server_name;
  char* uri;
#endif
} httpc_state_t;

In that same C file, it implements the following function, which uses the struct:

/** http client tcp poll callback */
static err_t
httpc_tcp_poll(void *arg, struct altcp_pcb *pcb)
{
  /* implement timeout */
  httpc_state_t* req = (httpc_state_t*)arg; // Here the void pointer is casted to httpc_state_t
  LWIP_UNUSED_ARG(pcb);
  if (req != NULL) {
    if (req->timeout_ticks) { // Here the concrete type is used. Works. No problems.
      req->timeout_ticks--;
    }
    if (!req->timeout_ticks) {
      return httpc_close(req, HTTPC_RESULT_ERR_TIMEOUT, 0, ERR_OK);
    }
  }
  return ERR_OK;
}

I have a C++ file which uses this library, and of course includes the required header (http_client.h).

extern "C"
{
    #include "FreeRTOS.h"
    #include "task.h"
    #include "semphr.h"

    #include "lwip/tcpip.h"
    #include "lwip/apps/http_client.h"  // Here I include their http_client.h file

    #include "projdefs.h"
}

In my next function, I need to do exactly what their implementation does. I need to do something with httpc_state_t. I implemented their callback function as follows:

err_t rec_fn(void *arg, struct altcp_pcb *conn, struct pbuf *p, err_t err)
{
    if (p)
    {
        httpc_state_t* req = (httpc_state_t*)arg; // Compiler sees no problems in casting to my desired type....
        req->timeout_ticks = 30; // COMPILE ERROR, pointer to incomplete class type _httpc_state is not allowed 
    }
}

Why am I getting that compile error?! Header file is included. Header files declares the typedef. Even after reading this and this, I still don't see what I am doing wrong....

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
bas
  • 13,550
  • 20
  • 69
  • 146
  • 4
    `httpc_state_t` hasn't been defined in the `.h` file so you can't access its members. This is probably an example pointer of an opaque pointer, meaning that the libaray is intentionally disallowing you from using `httpc_state_t`'s members directly. Look for any helper functions that can help you set `timeout_ticks`. – mediocrevegetable1 Jun 16 '21 at 10:16
  • 2
    The intention of the library is probably that you should **not** access the members of the structure in your code. Assuming you are referring to https://github.com/RT-Thread/IoT_Board/blob/master/rt-thread/components/net/lwip-2.1.0/src/apps/http/http_client.c, the structure of type `httpc_state_t` is allocated and initialized by `httpc_init_connection_common`. This includes the value of `timeout_ticks`. Why do you need to modify internal data of the library? – Bodo Jun 16 '21 at 10:55
  • @Bodo (and mediocrevegetable) thx for your comments. I guess you are right and that they don't want me to change the data. The problem is, I am downloading a "large" file (512KB) as a test, and that takes for longer than the timeout allows. I would expect that the http_client would reset the timer when the download is still going and packets are being received. But in their library the timeout counter only gets **decreased**. I can find no helper function or anything that allows me to control this behavior – bas Jun 16 '21 at 11:29
  • I commented out the line that decreases the counter (giving me an infinite timeout), then the download comes through all the way without any issues. But I must be missing something. They implemented the mechanism for a reason of course.... – bas Jun 16 '21 at 11:30
  • PS: the header file is here: https://git.savannah.nongnu.org/cgit/lwip.git/tree/src/include/lwip/apps/http_client.h?h=STABLE-2_1_x and the source file is here : https://git.savannah.nongnu.org/cgit/lwip.git/tree/src/apps/http/http_client.c?h=STABLE-2_1_x – bas Jun 16 '21 at 11:31
  • @bas You should add the background information to the question. (I assumed from your reputation that it was not necessary to mention this.) I think the `lwip-users` mailing list might be a better place for questions or suggestions about this timeout mechanism in combination with slow downloads. https://savannah.nongnu.org/mail/?group=lwip – Bodo Jun 16 '21 at 11:53
  • @Bodo Right, I will continue on the mailing list. The background to my question might be less relevant here. My question was about my lack of knowledge in C. Regardless of the real issue at hands, I learned something today :). Thx for the help – bas Jun 16 '21 at 12:11

3 Answers3

2

In the translation unit where the function rec_fn is defined the compiler sees only the following declaration

typedef struct _httpc_state httpc_state_t;

It knows nothing about whether the data member timeout_ticks used in this statement

req->timeout_ticks = 30;

is indeed declared within the structure struct _httpc_state and what is its type. That is the name timeout_ticks is undeclared in this translation unit. So the compiler issues an error.

If you are going to use data members of the structure in a translation unit then the compiler needs to know their declarations. That is you need also to include the structure definition.

Either move the structure definition in the header if you are allowed to do that or duplicate its definition in the module where your function is defined.

Pay attention to that if the structure definition was not placed in the header then the reason of that can be that the author of the code does not want to make it available outside his module or library.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • I tried your trick (duplicate def in my own source file), that works perfectly. I thought I would get errors of duplicate definitions, but I guess the linker figures that out. But as comments below my question also suggested, I guess I am going in the wrong direction doing this. – bas Jun 16 '21 at 11:34
1

Incomplete type means its declared but not defined. You need to define that struct in the header file and include it into your C file.

Sekomer
  • 688
  • 1
  • 6
  • 24
1

The error message is poorly worded.

Pointers to incomplete types are fine! Dereferencing to a member of an incomplete type is the problem.

At the point where the error occurs the compiler hasn't 'seen' the full definition of the type in that translation unit.

It recognises the type, but doesn't know if the type even has a member timeout_ticks let alone how to generate code for access it. [Such as where the member is in relation to the start of the object.]

Pointers to incomplete types are a useful way to reduce dependencies and code coupling. If code only needs to pass pointers to a type around the type can be declared (incomplete) and help with type checking but not be exposed to the full definition.

Persixty
  • 8,165
  • 2
  • 13
  • 35