Showing posts with label cpp. Show all posts
Showing posts with label cpp. Show all posts

Thursday, April 02, 2020

Writing my first C program in Linux

Recently I wrote my first C program in Ubuntu Linux for which I had a deadline. I was already familiar with C/C++ on Windows. It was quite a learning experience. My method was as follows:
  1. Search the internet for projects matching the problem definition. Result: There weren't any.
  2. Search examples/tutorials for terms in the problem definition like fork, poll.
  3. Write small demos. Modify and combine examples to solve the original problem. Result: Faced many difficulties during adaptation.
  4. Dive deeper and understand the details of concepts/terms that I am not well versed in, like pipes.
  5. Apply that deeper knowledge.
  6. Increase depth until the problem is solved in its entirety.
As you can see, I did not start by reading a lengthy Linux C fundamentals book. I focused on the problem at hand, tried shortcuts first and only spent more time on problematic points. I felt a lot of frustration while trying to hack my way out but I was determined enough to overcome constant failures. If you are working under time pressure, this is the fastest method of getting things done.

Notes:
  • To display processes starting with "node": ps -ef|grep node
  • To run as a background process, add & to the end.
  • Windows Subsystem for Linux, using Ubuntu with VS Code, tasks.json to build file (ctrl + shift+b)
  • pipes, socketpair, filedescriptor, dup2. 
  • Redirecting stdin
  • fork - exec
  • poll
  • Converting a string to a vector of strings, with space as delimiter
  • Difficulty of using strings. Writing and reading structures is much easier, to read a string after a write, you need to send EOF with close(fd), but then you have the problem of opening the filedescriptor again.
  • gcc, makefile, tar
  • Difficult to find bug in C: for (int i = 0; len; i++). Note that i< is missing.
  • When you print elements of a union structure, you will only get the last set element right.

Tuesday, December 31, 2019

Solving inheritance ambiguity with composition

Let's say you have the following C++ classes, making use of the observer pattern:

In implementation of class A, you call Notify(T) of CObservable.

When you compile, you will get a message that there is ambiguity in calling Notify(), because the compiler does not know if it should call Notify(T) with class T1 as a parameter or T2.

The solution is to remove CObservable from inherited classes and introduce a field, i.e. using composition over inheritance. You also need to have a registerT2Observer() method that in turn calls the register method of CObservable:

Letters to a novice programmer

Dear novice, this time it has been a short while, isn't it? When you have a dll that you call from both a Java app and C++ app with the same inputs but the outputs differ, do not suspect that there might be something wrong with the operating system or JNI. Your problems will always be due to something you did wrong.

You should write some debug code inside the C++ dll that prints dll inputs to a file. Run the dll from Java and C++ and compare dll inputs. I guarantee you that you will see a difference there because most probably you did something wrong before passing the inputs to dll like using a wrong index or making an erroneous unit conversion.

Another tip: When comparing files, never trust your eyes, always use a comparison tool like Total Commander's "Compare by content" or Beyond Compare.

Wednesday, October 09, 2019

Get SVN revision and write to header file

I am creating a C++ dll file from a MATLAB Simulink model. The simulink model repository is hosted on subversion. I needed a way to embed simulink model subversion revision to dll so that the dll could return that revision via an interface method to interested clients. I wrote the following batch file to fetch revision from SVN and write to a header file:

Wednesday, August 28, 2019

Problem with mixing C++ and C code

In a C++ project that also contained legacy C files, I started to get the error "C2664 ...cannot convert argument 3 from void * to const_locale_t". The argument in question was NULL. Changing the include order by including the legacy header after stdio.h solved the problem but I wanted to understand the root problem to avaiod unnecessary technical debt.

One of the C headers contained #define NULL ((void *)0) which is standard for C but not for C++. Normally NULL is defined inside vcruntime.h (part of Visual C++). Using the legacy NULL before any code that depends on vcruntime.h, like string.h, stdio.h, caused this error. Removing the custom definition or changing it to the following solved the problem, see also my answer on StackOverflow:
Another strange error happens if you have defined fmax globally in your own C code because it also exists in cmath. The strange error will be C2039 'fmax': is not a member of 'global namespace'. Removing or renaming your own custom definition will solve the problem.

Friday, May 31, 2019

Build Visual C++ Project from Command Line

Previously I showed how to build a couple of files from command line. To build a whole Visual C++ project from command line, I tried msbuild. However, I had no luck with it because I got "error TRK0002: Failed to execute command...". Then I used devenv and it worked perfectly. The following batch script did the job from a normal command line:

@DevEnvDir="c:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\IDE\"
@set PATH=%DevEnvDir%;%PATH%
@cd c:\temp\DeleteMe
@call devenv DeleteMe.sln /build "Release|x86"

Thursday, May 09, 2019

Visual C++ DLL

When coding a C++ project that uses an existing dll, you also need to have the lib file to be able to build your project. Once you have built your project and created your exe, you only need the dll, not the lib.

Bonus: stringstream is slow, char* is faster.

Wednesday, April 10, 2019

Windows: Build C++ from command line

If you have Visual Studio 2017, to build a C++ project from a "normal" - not developer - command line:
  1. Add the following to system path: 
    1. To use cl.exe: c:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Tools\MSVC\14.12.25827\bin\Hostx64\x64\
    2. To use vcvars64.bat: c:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\
  2.  Open a command window and issue the following commands
    1. vcvars64
    2. If you are in a drive other than C:, let's say D:, you have to issue the command D: because after vcvars64, the drive reverts to C:
    3. cl a.cpp b.cpp
For example, in order to build mms-cpp, I created a batch with the following content:

set PATH=%PATH%;C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Tools\MSVC\14.12.25827\bin\Hostx64\x64\;C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\
call vcvars64
d:
call cl API.cpp Main.cpp

Note that there should be no whitespace after "PATH=" and the additional two paths should not be enclosed with quotation marks.

Wednesday, November 14, 2018

Mixing C++ and C and calling a C/C++ dll from Java

In Visual Studio, it is possible to mix C++ and C code, C++ files should have cpp extension, C files should have C extension, the compiler takes care of the rest. When including a C header file inside a C++ file, wrap it inside extern "C { #include "somfile.h"}. Otherwise, you will get LNK2019 (the dreaded "unresolved external symbol" error).

When you want to create a dll from this mixed project to be used with Java, in your wrapper header file of cpp implementation, you have to wrap the header body inside #ifdef __cplusplus extern "C" { #endif ...  #ifdef __cplusplus } #endif. Otherwise you will be able load the dll in Java but when you call a dll method, you will get UnsatisfiedLinkError.

When naming your functions in C/C++ Java interface functions section, do not use underscores at the end of the function name like "Java_package_name_myFunction_rad_s()" because in Java interface functions, undescores are only used as package name separators. If you ignore this, you will get UnsatisfiedLinkError and wonder why.

Friday, April 04, 2014

Returning objects in C++

I recently discovered that returning an object in C++ might not be what you expect. I was surprised because in Java things would be different. Consider the following code:


getClassB() returns the mClassB attribute. Class B is as follows:


I wrote the following main function to test results:


I thought that a.getClassB().setVal(1) would set mClassB.mVal to 1. But when I read it using a.getClassB().mVal I got a meaningless number hinting that mVal was not set.

When I set the result of a.getClassB() to ClassB b and then set b.setVal(2) and read b.mVal I get 2 as expected.

Finally, when I use a.getClassBReference()->setVal(4) and then read it using a.getClassBReference()->mVal, I get 4.

I think that a.getClassB() returns a copy of mClassB which means that a setVal() operation on that copy does not modify a.mClassB.mVal. If you want to modify an field of an object attribute, the getter of that attribute has to return a reference, not a copy.

Saturday, April 16, 2011

Adventures with Visual Studio 2010

How to prevent console from exiting immediatly when you run using Ctrl + F5:
With the new visual studio 2010 you might see this behavior even when you use ctrl f5 aka "start without debugging". This is most likely because you created an "empty project" instead of a "Win32 console application". If you create the project as a "Win32 console application" you can disregard this as it does not apply.

In the older versions it would default to the console subsystem even if you selected "empty project", but not in 2010, so you have to set it manually. To do this select the project in the solution explorer on the right or left (probably is already selected so you don't have to worry about this). Then select "project" from the menu bar drop down menus, then select "*project_name* properties" > "configuration properties" > "linker" > "system" and set the first property, the drop down "subsystem" property to "console (/SUBSYSTEM:CONSOLE)". The console window should now stay open after execution as usual.
When you have a C++ project with C files in it, you will get "error D8045: cannot compile C file". You need to change a setting [MSDN]:

1. Open the project's Property Pages dialog box.
2. Click the C/C++ folder.
3. Click the Advanced property page.
3. Modify the Compile As property and set it to /Tp. The /Tp option specifies that filename is a C++ source file, even if it doesn't have a .cpp or .cxx extension. Which is the case for C files which usually have .c extension.

Friday, January 16, 2009

Judicious use of getline() in C++

When reading text files in C++ I use the getline() function. There are two getline() functions. One is a method of ifstream class. The other is a standalone function. I think it is either in string.cpp or istream.cpp. I am too lazy to look it up now. When you use ifstream.getline(), you have to specify the number of characters that you want to read. You have to make sure that you choose that number such that it is not less than the character count of the longest line in the file that you are trying to read. If it is less than that and you use "while (!ifstream.eof())", you run into an infinite loop! This is a big deal if you are writing a generic file manipulation library since you cannot know the maximum number of characters in a generic file. With the standalone getline() there is no such problem. I have written two functions that implement the above getline() functions: The text file I am reading is: The output is as follows: As you see, in the second method the number of characters is not sufficient to read "hello". It interestingly reads "hell" which is the place the program goes at that point I guess(!). Once that happens, the rest of the file cannot be read. It doesn't stop either. If I hadn't put the "if (lineNumber>maxNbOfLines)" block into the second function, it would not be able to detect eof and there would be an infinite loop. I don't exactly know the reason of this behaviour. To learn that I have to study details of getline() and eof()... and right now I am too lazy for that. Lesson learnt: Use the standalone version of getline()

Tuesday, April 29, 2008

C++ stack overflow

While searching for infinity and indeterminate in C++ a few days ago I came across a bizzare bug report that left me shievering and made me feel so powerless. Ironically it was a case where the power function (pow) was used, Pow function not working:

"When C++ compiler is compiling a function that returns double, it pushes the return value on the coprocessor stack (i.e. on ST0). If the caller ignored the return value, the compiler generates the instructions that free the stack of the coprocessor. But if we casted the function pointer to a void-returning function, the caller won’t free the coprocessor and therefore it will stack-overflow after a while."

I wrote something similar to the code mentioned in the above page:


As you can see, we made an error by casting a double returning function to a void pointer. But since that seemingly has nothing to do with the rest, everything should be fine and the code should not enter the "if (dValue != 100)" block. The output tells a different story:


Notice that after iteration 7, although dValue is 100 upto 17 digits, it still enters the "if (dvalue != 100)" condition. Interestingly, the assertion does not fail (which should, if it enters != 100)! But if you put the assertion just after line 22, the assertion will fail! For the reason of this strange bug, read the link above.

Now, how on Earth can you trace the origin of such a case in a large program? You need to disassemble the shit out of it! Everyday the evidence whispers to me that C++ should be avoided whenever possible. Especially if you are not a software engineer, but rather use software as a tool to explore physical phenomena (like me). ADA comes to mind, if only it had a cool IDE. Could C# be a second best to realize defensive programming? Or may be I should never write large software and be content with my toy programs ;)

Quote of the day:

"Everyone knows that debugging is twice as hard as writing a program in the first place. So if you're as clever as you can be when you write it, how will you ever debug it?" -- Brian Kernighan, "The Elements of Programming Style", 2nd edition

Monday, April 21, 2008

Dealing with infinite and indeterminate values in C++

In my programs infinity usually arises when a value is divided by zero. I get indeterminate when I divide zero by zero.

In C++, infinity is represented by 1.#INF. Indeterminate is represented by -1.#IND. The problem is how to test if a variable is infinite or indeterminate. Checking infinity is relatively straightforward: You find the infinity definition in your particular C++. For my case (VS2003), it is std::numeric_limits::infinity(). You have to include "limits" in order to use it. You can assign this infinite value to a variable and you can compare it to some value in order to check if that value is infinite.

Indeterminate is a little tricky, because you cannot compare an indeterminate value to some other value. Any comparison returns false. You can use this property to detect an indeterminate value by comparing it to itself. Let's say you have a double variable called aVal. Under normal conditions, aVal != aVal returns false. But if aVal is indeterminate, aIndVal != aIndVal returns true. This weird situation is not present for infinite values, i.e. aInfVal != aInfVal always returns false.

Here are two functions that can be used to check for indeterminate and infinite values:


Update Jan 04, 2009: I asked this question on Stackoverflow

Thursday, March 27, 2008

C++ Rant

A rant about C++: "...the solution is _not_ to design everything on paper first, then implement, although this is the way that C++ would actually have worked out well -- if it were _possible_ to design like that in our world. all experience has taught us that solving a complex problem uncovers hidden assumptions and ever more knowledge, trade-offs that we didn't anticipate but which can make the difference between meeting a deadline and going into research mode for a year, etc."