14

I have v1 and v2 versions of my software. v1 uses the registry to save settings, with lots of calls to GetProfileInt, etc. v2 now uses an sqlite db to save settings.

We are currently developing both branches and are merging new features from v1 to the v2 branch. We currently have to remember to update any registry calls to use the new config db and this has been missed a few times.

What I would like is to throw a compiler error if any of the GetProfile... or WriteProfile... functions are used in v2.

We're using C++ in Visual Studio 2010. If there's nothing built in can I use the output from a script to throw a compiler error somehow?

jacobsee
  • 1,438
  • 4
  • 18
  • 34
  • 7
    You could try to `#define` them to garbage code that won't compile? – Mysticial May 23 '12 at 20:36
  • Couldn't you re-declare them? – chris May 23 '12 at 20:37
  • What do you mean built-in? GetProfile, etc are no more built in than the ones for sqlite. – Nemanja Trifunovic May 23 '12 at 20:37
  • To add on to @Mystical's idea, #define GetProfile to something like dont_us_get_profile_use_this_other_thing_instead. – SirPentor May 23 '12 at 20:38
  • I'm probably a neanderthal, but I'd just bung in a test case that greps the source for `GetProfileInt`... – Steve Jessop May 23 '12 at 21:09
  • grep works, but if a code base spans a few GBs and hundreds of directories it can be a little bit boring grepping it. In such cases I recommend OpenGrok, it's basically a grep on steroids. – Gene Bushuyev May 23 '12 at 21:22
  • @GeneBushuyev: yeah, if my source takes significant time to grep for one token, then I'm going to start up another company to give me something to do while I wait for it to compile. But OpenGrok presumably does provide good ways to do this task: find all call sites to any of a given list of functions. – Steve Jessop May 23 '12 at 21:30
  • @Nemanja: By built-in I meant functions that I didn't write, maybe not the best terminology. – jacobsee May 23 '12 at 22:14

6 Answers6

13

Since this answer is accepted I might as well include the solution the asker actually used:

jacobsee discovered the deprecated pragma

#pragma deprecated(GetProfileInt)

Original answer:

You may be able to declare them as deprecated using __declspec(deprecated). It would look like this:

UINT __declspec(deprecated) WINAPI GetProfileInt(
  __in  LPCTSTR lpAppName,
  __in  LPCTSTR lpKeyName,
  __in  INT nDefault
);

You'll have to do so from a header that is included in every translation unit you care about. Doing so will result in a warning any time a translation unit that includes the deprecated declaration uses that function.

If you want a compiler error and if your project doesn't already treat warnings as errors then you'll have to turn that on, and maybe fix all the warnings you've been ignoring. (These are good practices whether you use this solution or not.)

bames53
  • 86,085
  • 15
  • 179
  • 244
  • Just FYI, `cl` has an option `/FI` that can be used to include a header without having to modify any source, might be useful here: `/FImydeprecated.h` – hmjd May 23 '12 at 21:14
  • Alternatively, one can put the function declaration in an anonymous namespace and if it's called compiler would report ambiguity. – Gene Bushuyev May 23 '12 at 21:17
  • I tried your code and it didn't work for me. I got a dll linker error. Then tried putting WINBASEAPI in front and that compiled. It wants to match the SDK version vs the CWinApp version I think?? But then it couldn't detect my use of AfxGetApp()->GetProfileInt. But I did find a solution that works great: Just add #pragma deprecated(GetProfileInt) to a global header file. I did the same for GetProfileString, WriteProfileInt, WriteProfileString. Thanks! – jacobsee May 23 '12 at 21:44
  • Here's a link that led me to the answer: http://weseetips.com/2008/12/22/how-to-enable-warning-for-deprecated-functions/ – jacobsee May 23 '12 at 21:52
  • I also set the project config C/C++\Advanced\Treat Specific Warnings As Errors = 4995 to treat pragma deprecated warnings as errors. We're not quite to the point of treating all warnings as errors but shooting for that goal. – jacobsee May 23 '12 at 21:54
3

Promoting my comment to an answer:

You can use a macro to redefine them to something that won't compile:

#define GetProfile  HAHA_Nice_try_This_will_not_compile!!!

The catch is that you need to make sure that it isn't (legitimately) called outside your code.
(So you should put the macro after all your includes.)

Mysticial
  • 464,885
  • 45
  • 335
  • 332
  • Macros are usually bad idea, it will do textual substitution everywhere, even if one has a legitimate GetProfile name, say as a member function. It's a lot safer to declare the functions, not the macros. – Gene Bushuyev May 23 '12 at 21:05
  • Good point. I forgot to consider member functions. Though aside from bames53's answer, I can't think of a way you could redeclare the function and get it to compile at all? – Mysticial May 23 '12 at 21:11
  • @Mystical: you can use anonymous namespace instead of `deprecated` – Gene Bushuyev May 23 '12 at 21:18
  • @GeneBushuyev Oooh... that's tricky! :) I was trying to think of something along the lines of an ambiguous overload. But namespace hacking is better! – Mysticial May 23 '12 at 21:21
  • macros you work if you actually need that function declared. Eg: overloading the delete operator. Also macros pollute the namespace as they know no namespace. – user13947194 Sep 24 '22 at 20:03
3

If you're free to turn the function into a template, you can do something like this:

template <typename...>
struct always_false { static constexpr bool value = false; };

template <typename... Ts>
void never_call_me(Ts&&...)
{
  static_assert(always_false<Ts...>::value,
                "You should have never called this function!");
}

This has the advantage that the compile error will be clean + you can provide an error message. Found here, see that answer for more info on why this works + why always_false is needed.

Adversus
  • 2,166
  • 20
  • 23
  • 3
    This should be the accepted answer. "=delete" can also be used if its is a member function of a class that needs to emit an error when called. – John Z. Li Aug 31 '21 at 02:16
  • As a note, Visual Studio 2010 didn't support `constexpr`. If the OP is still stuck supporting VS2010, they'd want to use an old-school `enum` constant instead. – Justin Time - Reinstate Monica Jul 22 '23 at 17:40
2

The accepted answer is to mark the functions as deprecated, but that doesn't really fit what the question is asking, for two reasons:

  • It only gives a warning, not an error.
  • It will give warnings even if you're using the v1 code.

There's good reasons to want that, but it's not actually what the original question asks for.

Luckily, there's a really easy way to get what the questions asks for. The compiler will always throw an error if the function simply doesn't exist. Just throw the functions in an #ifndef.

#ifndef V2

void GetProfile()
{
  // Get the profile
}

void WriteProfile()
{
  // Write the profile
}

#endif
Rick Yorgason
  • 1,616
  • 14
  • 22
1

If you have an #include file that is common, you could add something like this to it (with the W or A as appropriate). It would result in, at least, a linker error (compiler warning/error would depend on the flags):

#define GetProfileIntA InvalidFunctionDoNotCallMe

Windows already defines the functions such as GetProfileInt as macros to the A or W versions.

Mark Wilkins
  • 40,729
  • 5
  • 57
  • 110
  • 1
    You don't need an include file that's common, simply use /FI to force include a file in all cpp files. You can set /FI in the project file. I blogged about this here: http://www.lenholgate.com/blog/2004/07/fi-stlport-precompiled-headers-warning-level-4-and-pragma-hdrstop.html – Len Holgate May 23 '12 at 20:49
  • @LenHolgate: That's a good bit of information. I was not aware of that /FI switch. – Mark Wilkins May 23 '12 at 20:52
  • @LenHolgate: doesn't `/FI` include the file at the *start* of each TU, though? So the `#define` will also be in effect in the header where `GetProfileIntA` is declared. At best you'll get a linker error, at worst `GetProfileIntA` is an inline function, and everything still works! – Steve Jessop May 23 '12 at 21:02
  • @Len Holgate - I really don't like the idea of burying include files in project options. I had enough trouble in the past digging out stuff people redefined or hidden from plain view. I would rather require including header everywhere, it's a small price to pay for readability. – Gene Bushuyev May 23 '12 at 21:34
  • Gene, given the original questions requirements I thought it worth mentioning. I agree with you though, it's not a practice that I'd like to use on a long term basis. It can be useful though. – Len Holgate May 24 '12 at 05:50
0

I believe you could #define GetProfileInt(a, b, c) "don't use this"; after #include'ing Windows.h.

Since GetProfileInt is a macro for routing to the proper function, this would result in a macro redefinition. And since char[] can't be assigned to UINT, the compiler error's.

It's a dirty, dirty hack though, I feel like taking a shower for discussing it.

anthonyvd
  • 7,329
  • 4
  • 30
  • 51