1

I don't really understand what Visual Studio 2019 wants from me (C language). On one hand it doesn't throw me either error or warning, but on the other hand it marks me with a green squiggling the createCustomer decaration in my API file. I would like to encapsulate Customer and use ADT on Customer structure.

These are my source and header files:

customer.h

#include <stdlib.h>

typedef struct Customer
{
    unsigned int customerId;
    const char* name;
    int numOrders;
}Customer, * CustomerPtr;

customer.c

#include "customer.h"

#define MAX_NO_OF_CUSTOMERS 10
static Customer objectPool[MAX_NO_OF_CUSTOMERS];
static unsigned int customersCount = 0;

CustomerPtr createCustomer(const char* name, size_t numOrders)
{
    CustomerPtr newCustomer_p = NULL;
    if(customersCount < MAX_NO_OF_CUSTOMERS)
    {
        newCustomer_p = objectPool + (customersCount++);
        newCustomer_p->name = name;
        newCustomer_p->numOrders = numOrders;
    }
}

customer_api.h

#include "customer.h"

CustomerPtr createCustomer(const char* name, int numOrders);

main.c

#include <stdio.h>
#include "main.h"
#include "customer_api.h"

int main()
{
    CustomerPtr customer_p = createCustomer("Demo", 5);
    return 0;
}

So as I said, under customer_api.h the following CustomerPtr createCustomer(const char* name, int numOrders); has squiggle under the function declaration createCustomer. The program compiles and run successfully without errors/warnings.

Maybe I'm not using correctly the API h file concept? I'm trying to keep the customer properties implementation hidden from external files, so in other files I just include customer_api.h anytime I need to approach the customer module.

Adrian Mole
  • 49,934
  • 160
  • 51
  • 83
IntToThe
  • 59
  • 6
  • 2
    Just activate warnings, `createCustomer` doesn't return anything – Ôrel Jan 03 '22 at 14:47
  • 2
    If you hover over the squiggles, what message pops up? – the busybee Jan 03 '22 at 14:47
  • 2
    Your declaration has an `int` second argument but your definition has `size_t`. Different functions. – Adrian Mole Jan 03 '22 at 14:49
  • @AdrianMole - Excellent. Solved it. However, is my implementation being good practice for encapsulation? – IntToThe Jan 03 '22 at 14:51
  • 1
    But as @Ôrel has pointed out, the function is ill-formed and, as far as I can see, the code you've posted really can't (or shouldn't) run successfully. (It probably *appears* to work because you aren't attempting to use the pointer that **isn't** returned by the call in `main`.) – Adrian Mole Jan 03 '22 at 14:51
  • 1
    "I'm trying to keep the customer properties implementation hidden from external files..." You cannot hide anything that you put in a header file, even though it goes by a different name. The proper way of dealing with this issue is forward-declaring your `Customer`+`typedef` for `CustomerPtr` in the header, writing the APIs in customer.h referring to only `CustomerPtr`, and defining the `struct` in the `customer.c` file. – Sergey Kalinichenko Jan 03 '22 at 14:52
  • @Ôrel and Adrian - yes, I should have returned Customer pointer (the program still works). – IntToThe Jan 03 '22 at 14:54
  • 1
    "the program still works" Unlike other languages, an ability of a C program to run to completion without crashing does not necessarily mean that it "works". In your case, the program without a `return` has undefined behavior, meaning that `customer_p` in your `main` ends up with some junk value. – Sergey Kalinichenko Jan 03 '22 at 14:58
  • 1
    This isn't how you encapsulate structs proper. Check out [How to do private encapsulation in C?](https://software.codidact.com/posts/283888) Also I would never advise to hide pointers behind typedef. `CustomerPtr` is just an obfuscated way of writing perfectly readable `Customer*`. – Lundin Jan 03 '22 at 15:01
  • @Lundin thanks. Why not to hide pointers behinde typedef? – IntToThe Jan 03 '22 at 15:03
  • @IntToThe It's a big topic, but briefly, it makes the code much harder to read and debug. – Lundin Jan 03 '22 at 15:04

2 Answers2

2

On one hand it doesn't throw me either error or warning …

You should enable all warnings in your project settings; see: Why should I always enable compiler warnings? With warnings enabled, your code (or a version of it I pasted into my VS 2019 IDE) generates 5 warnings:

warning C4255: 'main': no function prototype given: converting '()' to '(void)'
warning C4189: 'customer_p': local variable is initialized but not referenced
warning C4028: formal parameter 2 different from declaration
warning C4267: '=': conversion from 'size_t' to 'int', possible loss of data
warning C4716: 'createCustomer': must return a value

Now, #1, #2 and #4 can be put aside (for now); the big issues are #3 and #5.

To address #3: You need to make the arguments in your function definition (in "customer.c") the same as in the prototype (in "customer_api.h"); presumably, as the numOrders member of your structure is an int, you should change the former to have an int second argument (this also addresses and removes warning #4):

To address #5: The createCustomer function must return what it is declared to return: a CustomerPtr object. The local variable, newCustomer_p, is the obvious candidate for this.

Here is a 'fixed' version of that function:

CustomerPtr createCustomer(const char* name, int numOrders)
{
    CustomerPtr newCustomer_p = NULL;
    if (customersCount < MAX_NO_OF_CUSTOMERS) {
        newCustomer_p = objectPool + (customersCount++);
        newCustomer_p->name = name;
        newCustomer_p->numOrders = numOrders;
    }
    return newCustomer_p;
}

This leaves warnings #1 and #2. To fix #1, add void as the argument list for main (the empty parentheses don't actually define a function in C). Presumably, you'll be doing something with the unused variable, at some point, so that will address #2.

int main(void) // Note the EXPLICIT void argument list; () just means "unspecified" in C
{
    CustomerPtr customer_p = createCustomer("Demo", 5);
    // Do something with "customer_p"
    (customer_p);
    return 0;
}

… but on the other hand it marks me with a green squiggling.

Hovering the mouse over the squiggles will show the problem: "Function definition for 'createCustomer' not found." This is caused by the issue that generates warning #3; fixing that resolves the green squiggles.

Adrian Mole
  • 49,934
  • 160
  • 51
  • 83
1

To keep the implementation of the type Customer hidden from the client module you need to put the structure definition in the C file. Here is one way to do it. In the code below I call CustomerPtr simply Customer because I think it is cleaner. For the sake of simplicity and consistency it's a good practice to use the same name of the customer implementation and header file as the customer data type. I also recommend using the prefix Customer_ for each function in Customer.h. I have also made a few other adjustments.

Customer.h

#ifndef CUSTOMER_H
#define CUSTOMER_H

#include <stdlib.h>

typedef struct CustomerDesc *Customer;

Customer Customer_Create(const char *name, size_t numOrders);

#endif

Customer.c

#include "Customer.h"

#define LEN(array) (sizeof (array) / sizeof (array)[0])

struct CustomerDesc {
    unsigned int customerId;
    const char *name;
    int numOrders;
};

static struct CustomerDesc objectPool[10];
static unsigned int customersCount = 0;

Customer Customer_Create(const char *name, size_t numOrders)
{
    Customer newCustomer = NULL;
    if (customersCount < LEN(objectPool)) {
        newCustomer = objectPool + customersCount;
        newCustomer->name = name;
        newCustomer->numOrders = numOrders;
        customersCount++;
    }
    return newCustomer;
}

main.c

#include "Customer.h"

int main(void)
{
    Customer customer = Customer_Create("Demo", 5);
    return 0;
}
August Karlstrom
  • 10,773
  • 7
  • 38
  • 60