0

I need to use the C library in a C# project. How can I do?

To be more specific: for reasons of efficiency I need to use the strtod function to extract double values from a string (like this "9.63074,9.63074 -5.55708e-006 0 ,0 1477.78"). If you have suggestions about how to optimize this operation do not be shy, but the main question remains that specified by title.

gliderkite
  • 8,828
  • 6
  • 44
  • 80
  • 7
    "for reasons of efficiency I need to use strtod " - nonsense. The conversion of string to char* would take more time. And then you would have to convert doubles the other way. – H H Apr 18 '12 at 17:27
  • 2
    Maybe this will help: http://samcragg.wordpress.com/2012/01/26/strtod-in-c-part-1-the-specification/ – Quintium Apr 18 '12 at 17:30
  • 1
    @HenkHolterman You're wrong, the conversion of string to char* takes place only once, but then i can use char* in a loop. Do you know strtod function? Anyway i wrote that if you have any suggestion you can give me other solutions. – gliderkite Apr 18 '12 at 17:32
  • 2
    First suggestion: Measure with a profiler. Is `double.Parse()` really your bottleneck? – H H Apr 18 '12 at 17:35
  • @HenkHolterman thanks for suggestion, my string is like this "9.63074,9.63074 -5.55708e-006 0 ,0 1477.78", so to get all numbers I thought that strtod was better than to use double.parse() (that I use now). – gliderkite Apr 18 '12 at 17:38

5 Answers5

6

I think it very unlikely that p/invoking to strtod would be more efficient than a pure C# solution. There is an overhead in managed/unmanaged transitions and I would think that would be significant for something as trivial as strtod. Myself I would use a C# tokenizer, combined with double.Parse.

The simplest C# tokenizer is String.Split() which yields this routine:

static List<double> getValues(string str)
{
    List<double> list = new List<double>();
    foreach (string item in str.Split(default(Char[]), StringSplitOptions.RemoveEmptyEntries))
        list.Add(double.Parse(item));
    return list;
}

However, since I enjoy p/invoke, here's how you would call strtod from C#, bearing in mind that I recommend you don't use this approach in real code.

[DllImport(@"msvcrt.dll", CallingConvention=CallingConvention.Cdecl)]
static extern double strtod(IntPtr str, ref IntPtr endptr);

You can call it like this:

IntPtr str = Marshal.StringToHGlobalAnsi(inputStr);
IntPtr endptr = IntPtr.Zero;
double val = strtod(str, ref endptr);
Marshal.FreeHGlobal(str);

I'm passing the string as an IntPtr because you would be calling strtod repeatedly to walk across the entire buffer. I didn't show that here, but if you are going to make any use of endptr then you need to do it as I illustrate.

Of course, to use strtod remotely effectively you need to gain access to the errno global variable. The very fact that you need to deal with a global variable should be warning enough that here be dragons. What's more, the error reporting offered through errno is exceedingly limited. However, if you want it, here it is:

[DllImport(@"msvcrt.dll", CallingConvention = CallingConvention.Cdecl)]
static extern int _get_errno();

One final point. Your suggested input string is

"9.63074,9.63074 -5.55708e-006 0 ,0 1477.78"

but strtod won't tokenize that because of the spurious commas.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • Do Microsoft still have errno as a global variable? Modern runtimes have it in TSS for a long time... –  Apr 18 '12 at 17:39
  • @VladLazarenko "msvcrt.dll" is not a modern runtime. It's MSVC6. I honestly don't know whether or not it is thread local there. I really can't endorse the approach that is requested in the question. – David Heffernan Apr 18 '12 at 17:40
  • Cannot agree more with you on this one! –  Apr 18 '12 at 17:40
  • @DavidHeffernan which approach would you suggest? – gliderkite Apr 18 '12 at 17:43
  • 1
    @gliderkite I'd use a C# tokenizer, combined with `double.Parse`. Having said that, it's good fun to try to use `msvcrt` (well, I find it fun), and I'm just going to have a bash at reading out `errno`. – David Heffernan Apr 18 '12 at 17:45
  • @DavidHeffernan commas can be removed. – gliderkite Apr 18 '12 at 18:07
  • @DavidHeffernan Yours is a good answer, but I had already tried your alternative, which is not fast enough. I will open a new discussion more specific about my problem, I hope you're there. – gliderkite Apr 19 '12 at 08:37
2

First, you are not appreciating the fact that calls between managed and unmanaged code are relatively expensive, it is not free. So, from what you have told us, it sounds like you will be calling strtod many times from managed code. You'd have to test it, but the pinvoke penalty may negate any performance gains.

Also, have you actually tested to ensure that strtod is faster in your use case than the managed version would be? My testing showed that double.Parse is actually faster over 100,000 iterations. (strtod took ~54ms, double.Parse took ~15 over three runs. I can give you the code if you like.)

This sounds like a misguided attempt at optimizing a piece of code that hasn't even been tested. Are you sure you're solving the right problem?

Ed S.
  • 122,712
  • 22
  • 185
  • 265
  • No i'm not sure, infact I wanted to try making your own tests, and then I asked the question. Understood? If you think there's another solution i'm here. – gliderkite Apr 18 '12 at 17:47
  • @gliderkite: What my test showed is that `double.Parse()` was faster than `strtod` if only used for parsing a string into a double `strtod` can do more than that, but you didn't mention your use case). There is a penalty to be paid when crossing the managed/unmanaged code boundary. Instead of posting only your proposed solution you should also post what problem you are trying to solve. There may exist a solution that you have not yet thought of. – Ed S. Apr 18 '12 at 17:56
  • A question like [this](http://stackoverflow.com/questions/10053449/extract-numbers-from-string) or [this](http://stackoverflow.com/questions/10161572/parse-without-string-split)? Could i see your test? – gliderkite Apr 18 '12 at 18:01
  • @gliderkite: The key part of my question to you was what is the performance comparison *in your use case*. At the time, no one knew what your use case was (i.e., are you using `strtod` only to parse a string, or do you also need the secondary functionality of returning a pointer to the end of the number?) Since you have added more information in your comments, my test is no longer relevant. You are certainly free to perform your own test, it's not hard. For the C part, use the `QueryPerformanceCounter` functions in `windows.h` – Ed S. Apr 18 '12 at 22:45
  • As I'd already written, [this](http://stackoverflow.com/questions/10053449/extract-numbers-from-string) is my case. Would you do me a pleasure if I could look at the code, just as you wrote me. – gliderkite Apr 19 '12 at 08:41
1

Why not use double.Parse()?

double asDouble = double.Parse(myInput);
Richard J. Ross III
  • 55,009
  • 24
  • 135
  • 201
0

To answer the question of how to use a C library in .NET: One thing that you can do is to create a function the C calls that you desire. From there, you'll need to build it as a DLL. Once that is done, you'll be able to P/Invoke it from C#.

http://www.codeproject.com/Articles/9826/How-to-create-a-DLL-library-in-C-and-then-use-it-w http://msdn.microsoft.com/en-us/library/ms235282(v=vs.80).aspx

Another possibility is to build this as a mixed assembly.

http://msdn.microsoft.com/en-us/library/x0w2664k.aspx

That said, in your specific case, I would highly suggest looking at double.Parse() unless there is good reason otherwise.

David Z.
  • 5,621
  • 2
  • 20
  • 13
0

David showed you how to do it via p/invoke. However, the .NET way to do it would be to use Regex. Something like the following might work for you:

    MatchCollection matches = Regex.Matches(inputString, @".*?([-]{0,1} *\d+.\d+)");
    List<double> doubles = new List<double>();
    foreach (Match match in matches)
    {
        string value = match.Groups[1].Value;
        value = value.Replace(" ", "");
        doubles.Add(double.Parse(value));
    } 

This code will add all the doubles in a string into a generic List collection. Note I didn't test the Regex code against your string, but this gives you an idea.

Icemanind
  • 47,519
  • 50
  • 171
  • 296
  • Thanks, [i've used](http://stackoverflow.com/questions/10053449/extract-numbers-from-string) regex, but a regex is inefficient. – gliderkite Apr 18 '12 at 18:37
  • @gliderkite - Is speed a big issue for you? If so I might be able to come up with a better solution that is speed efficient – Icemanind Apr 18 '12 at 23:53
  • I will open a new discussion more specific about my problem, I hope you can help me to solve the problem there. – gliderkite Apr 19 '12 at 08:38
  • @VladLazarenko - All I meant is that it is better to use a .NET library to solve this problem then to use an unmanaged source. I'm not necessarily saying RegEx is the magical all-in-one way to solve a .NET problem. – Icemanind Apr 20 '12 at 22:33
  • @icemanind [this](http://stackoverflow.com/questions/10261594/string-to-listdouble#comment13194988_10261594) is my new post. – gliderkite Apr 21 '12 at 23:27