0

I'm trying to make my own math library with support for keeping stats on how many times each mathematical function in used. I would like all functions to have the same name as those in cmath so that I can easily just replace my math library header with cmath in my code and have it work the same way, just without the stats.

So far I've tried this:

my_math.hpp

float tanf(float ang);

my_math.cpp

#include <cmath>

float tanf(float ang)
{
    std::cout << "Custom tanf() used..." << std::endl;

    return std::tan((float)(ang));
}

Is this the best approach or is there a better one?

I've tried to use return std::tanf(ang) but get the error that "tanf is not a member of std", even though I can use tanf in other parts of the code where I don't have my own tanf declared (I can never use std::tanf though).

Is there a way to do this so that I can declare my own functions with the same name as the ones in cmath, add some functionality and then use the cmath version in the return?

Cascades
  • 627
  • 7
  • 18
Jesper
  • 60
  • 5
  • 2
    You should put all your functions in `namespace jesper { ... }`, to avoid collisions. – Eljay Apr 04 '22 at 19:32
  • Wouldn't I need to change all occurences in the rest of the code from jesper::tanf to tanf if I later decided to use cmath? – Jesper Apr 04 '22 at 19:36
  • I would agree that it would be normal to put your methods in a namespace and then, yes, you would have to change all `jesper`s to `std`s when you want to switch over. If you wanted things to be quick to change, you could perhaps write wrapper/helper functions in your code which decide, either at run-time or compile-time, whether to either call `jasper::tanf` or `std::tanf`? With a well optimised compiler I imagine you wouldn't see too much performance loss either, if any. – Cascades Apr 04 '22 at 19:39
  • 1
    A little bit of pain to eliminate the potential for confusion is worth it. – user4581301 Apr 04 '22 at 19:39
  • This doesn't work as I expected. I've added namespace jesper {...} in the header and then declared each function as jesper::func in the .cpp file. But now when I call the cmath function in the .cpp (without jesper;: before it) it still uses that function and ends up in an infinite loop. Can I get around this somehow? – Jesper Apr 04 '22 at 19:55
  • 3
    What you are trying to do is called “interposing” or “interposition”. Search for those terms to find information, including questions and answers on Stack Overflow. It is system-dependent; you should indicate whether you are doing this on macOS, Linux, Windows, or something else. – Eric Postpischil Apr 04 '22 at 20:06
  • 4
    You could have `-Dmath=std` or `-Dmath=jesper` and flip at compile time. The code would use `math::tan(ang)`, or have `using math::tan;` near the top, and call `tan(ang)`. – Eljay Apr 04 '22 at 20:07

1 Answers1

2

To expand on my comment, and presuming this is part of some test-framework type thing, this could work how you want?

// my_math.hpp
namespace jesper
{
    float tanf(float ang);
    /* more functions declarations */
}
// my_math.cpp
#include "my_math.hpp"
float jesper::tanf(float ang)
{
    /* function definition */
}
/* more function definitions */
// my_test_code.cpp
#include <cmath>
#include "my_math.hpp"

constexpr bool use_my_math = true; // switch this to change between std and jesper in helper functions

float generic_tanf(float ang)
{
    if constexpr (use_my_math)
        return jesper::tanf(ang);
    else
        return std::tanf(ang);
}
/* more helper functions */

int main()
{
    generic_tanf(0.1f);
}

This style would switch all your functions at compile time, you could also use other methods for compile time checks and re-organise things nicely. Or you could do things at runtime with similar helper functions.

You could perhaps also do something more akin to what I think you are asking for using horrible using namepsace std type stuff, but this is usually advised against for the reason you're wanting it, and it would most likely go badly. Name collision is not usually desirable!

Cascades
  • 627
  • 7
  • 18
  • 1
    Perhaps I'll comment here that the stuff I'm thinking you might be able to do but would not be advised is: `#ifdef use_my_math\nusing namespace jesper\n#else\nusing namespace std\n#endif` This would again, be compile time, but would also be further reaching than you want, the compiler would have no idea that this ever happened, and may bring pain to anyone consuming this in the future if it's not a tiny self-contained program. – Cascades Apr 04 '22 at 20:00
  • Thank you for pointing this out, I am equally interested in solving the problem as I am in solving it in a manner considered good practice! So I'll avoid the "bad way". Thank you for a detailed answer! – Jesper Apr 04 '22 at 20:12
  • 1
    You could also remove the helper functions and inline their code into your `my_math` code. The comments on your question seem to provide a few pointers in different directions, and I think that's because there's probably lots of ways of doing what you want. I'd say try out something like my suggestion or something like Eljay's suggestion, see which is best for your use case, and run with it. Good luck! – Cascades Apr 04 '22 at 20:15
  • Edit, corrected second link. – Cascades Apr 04 '22 at 20:17