0

I am working on a project in C Visual Studio, and I have two sets of functions, let’s call them SET_1 and SET_2.

I wonder if there is a way to ensure that a function from SET_1 calls only functions from SET_1 and not functions from SET_2.

The simplest solution will be to split the project in 2, but I want to avoid any major refactoring. I probably can make some runtime checks but I want to avoid this too…

So, I am wondering if there is something like SAL annotations that I can use to enforce this isolation at compile time?

Here is an example of what I want:

#define SET_1 ...
#define SET_2 ...

SET_1
void Fct1()
{
    // ...
}

SET_1
void Fct2()
{
    Fct1(); // Ok, both functions have SET_1 tag
}

SET_2
void Fct3()
{
    Fct1(); // Compile error, Fct1 has a different tag
}

I don’t want to write some kind of code parser to manually enforce this rule.


I have multiple files and a file contains functions from both sets. The functions don’t have any common characteristic, I manually need to specify the set for each function.

The solution can be at compile time or at build time. I just want to make sure that a function from set1 will not be called from set2

I can modify the code and I know that the right solution will be to refactor the project, but I am curious if there is another solution.

For example, if the code was in C++, I could include all functions from set1 inside a namespace and those from set2 inside another namespace. But this will not work if we have a class with function members in different sets.

CCrisan
  • 64
  • 5
  • 3
    My suggestion: don't give them similar names, and use a naming convention. It's not possible (even misleading) to tell that `Fct2()` is in set 1. So perhaps `set1_dothis()` and `set2_dothat()` and so on. – Weather Vane Mar 29 '22 at 08:44
  • 1
    C doesn't offer anything alike – and even in C++ you cannot achieve that. You could try static class member functions, but as soon as these are visible from public range (I assume you want to be all of these functions to be callable from outside the sets) they are visible to other classes as well... – Aconcagua Mar 29 '22 at 09:27
  • 2
    Call the sets S1 and S2. Before each function in S1, insert `#include S1.h`. Before each function in S2, insert `#include S2.h`. Create file `S1.h` that contains, for each function f in S1, `#undef f` and, for each function f in S2, `#define f DoNotUseThisFunction`. Create file `S2.h` similarly. Then the compiler will produce warnings for undeclared symbols if any function in S1 references a function in S2 or vice-versa. The macro replacement tokens can also contain the function name or can contain grammatically invalid tokens to cause other compiler errors. – Eric Postpischil Mar 29 '22 at 10:36
  • Define “split the project in 2”. In two what? Two projects? Two files? You could put the functions into two separate files and use `nm` or equivalent to detect whether the object files contain references to functions in the other set. Do you really want to do this only at compile time, or is any time during the project build okay? How are the function distinguished? Do you only have an arbitrary list of names for the members of each set, or is there some common characteristic of them? – Eric Postpischil Mar 29 '22 at 10:37
  • "but I want to avoid any major refactoring" Well these things need to be established during the program design stage before you write a single line or code. Otherwise you will very likely ending up rewriting the whole thing multiple times. – Lundin Mar 29 '22 at 10:45
  • Anyway it seems like you should make all of these functions `static` and place them in their respective .c files. Then use setter/getter functions to communicate with the rest of the world. And obviously using sane naming with unique prefixes, like you should do pretty much everywhere in your code. – Lundin Mar 29 '22 at 10:48
  • @Lundin: Nothing in the question indicates the functions should be declared `static`. It does not say they are only called from within the module they are defined in or indicate any reason there should be a layer between them and their callers. – Eric Postpischil Mar 29 '22 at 12:23
  • @EricPostpischil _Everything_ in the question indicates that `static` would solve it, since that's the common way of achieving private encapsulation of functions in C. – Lundin Mar 29 '22 at 12:41
  • 1
    @Lundin: No, the information in the question does not indicate that `static` would solve their problem. The function definitions are apparently in one source file that they do not want to refactor, so declaring them `static` would not isolate them from each other, but it would have the effect of breaking the ability to call the functions from other source files, which the question does not indicate is permissible. – Eric Postpischil Mar 29 '22 at 13:06
  • @WeatherVane using naming convention is a good solution but this way the rule is not enforced, for example from `set1_dothis()` I am able to call `set2_dothis()`. I know that I can define all functions from set2 inside a `set2.h` and where I implement the `set1_dothis()` to not include the `set2.h`, but I was trying to avoid refactoring, plus I was curious if there is any solution for this specific problem. – CCrisan Mar 29 '22 at 13:08
  • 2
    As suggested above, if there comes a point in your project development when you realise that you are having to *fight* your way forwards, then it's time to backtrack, take another course of action and write it off to experience. It might seem like a lot of work, but is often *less* work than forcing your way uphill, with increasingly "kludged" code. – Weather Vane Mar 29 '22 at 13:13
  • @Aconcagua for C++, I think namespaces will be a good solution, to include all set1 functions inside a namespace and all set2 inside another namespace, and if you don’t have `using namespace set1;` at the beginning of the file, I think it will work. But this will not work in the case you have in the same class member functions from different sets. – CCrisan Mar 29 '22 at 13:16
  • One idea: have a compile-time devlopment option, which does the checks at runtime. – Weather Vane Mar 29 '22 at 13:18
  • @EricPostpischil If the files can't be changed, then either the functions already call each other or they don't, and then nothing can be done about it. That would make the whole question meaningless. – Lundin Mar 29 '22 at 13:25
  • 1
    Please edit the question to answer the questions asked: Does “split the project in 2” mean splitting it into two projects or splitting the source file into two source files or something else? Do you really want this only at compile time, or is any time during the project build okay? How are the functions distinguish, by arbitrary list of names only or by some other characteristic? Explain more about the context and purpose of this. – Eric Postpischil Mar 29 '22 at 13:26
  • @Lundin: If `foo` calls `square(x)`, it can be rewritten to call `multiplybyself(x)`. There is no constraint in the problem about changing the source code of the functions. – Eric Postpischil Mar 29 '22 at 13:27
  • @EricPostpischil I will have to check your idea with `#define` and `#undef`. – CCrisan Mar 29 '22 at 13:29
  • 1
    @Lundin right now, I have these functions in multiple files and a file contains functions from both sets so using static will not resolve the problem. – CCrisan Mar 29 '22 at 13:34
  • @EricPostpischil It does not have to be at compile time, at build time it is ok, as long as I don’t have to write my own parser. I am not familiar with `nm`, but if the functions need to be in separate file, it will not work, because I have `set1` and `set2` functions in the same file – CCrisan Mar 29 '22 at 13:39
  • @CCrisan, using namespaces would not offer any more protection than simply prefixing the names with set-designating prefixes would. If a function from one set wanted to call one from the other set, it could still do so by using its namespace-qualifed name. – John Bollinger Mar 29 '22 at 14:25
  • Using the preprocessor would give somewhat stronger protection, but by no means complete. A function from one set that was determined to call one from the other set could still do so by adding its own `#undef` directives. So the question is: *against what* are you protecting? If you are trying to avoid *inadvertent* calls from functions of one set to functions of the other then name prefixing is about as far as I would want to go, myself. If you are trying to avoid *intentional*, even malicious, calls from one set into the other then you are out of luck. – John Bollinger Mar 29 '22 at 14:31
  • @JohnBollinger I am trying to protect against accidental calls only. Let’s say that I have the above three functions, if I include the `Fct1` and `Fct2` inside the namespace `set1` and `Fct3` inside de namespace `set2`, I only need to add the function declaration inside the namespace and add the namespace to the function name without modifying the function body. Then, inside the `Fct2` where I call `Fct1()`, `set1::Fct1()` will be called, but inside the `Fct3` it will try to call `set2::Fct1()` which doesn’t exist, so I will have a compile error. – CCrisan Mar 29 '22 at 15:54
  • If I will use a prefix to specify the `set1` or `set2`, nothing will stop me from accidentally calling from `set1` a function from `set2`, I am trying to find something to enforce this rule… – CCrisan Mar 29 '22 at 15:56

0 Answers0