Link to home
Start Free TrialLog in
Avatar of NickRepin
NickRepin

asked on

MS Visual C++ 6.0 extensions

I'm moving from Borland C to MS one and have to fight with this programmer-hostile product (I mean MS Visual C++ 6.0).

Here is the test program
----------
void a()
{
   char *p;
   ((int* ) p)++;  
}
----------
The code above compiles fine in C mode, but generates compile error 'Lvalue required' in C++ mode.

I should note that there are no problems with the code above on Borland C at all.

I tried /Ze, /Za switches or no switches at all. I tried IDE with appropriate checkbox checked and unchecked.

So the Q is: how to make VC compile the code above in C++ mode?

Please, do not suggest solutions like
p = ( char * )(( int * )p + 1 );

I will not accept answers like 'It is impossible'.
 
Avatar of nietod
nietod

>> I'm moving from Borland C to MS one and have to
>> fight with this programmer-hostile product
I've been there.  You'll be glad you did in the long run, although every once and a while....

It is impossible.   and for good reason.  When you cast the pointer to a new type a temporary pointer is (potentianly) created  (yes, its not actually necessary for this case, but this applies to any cast and there are casts where it would be necessary)  You cannot change the temporary as changes to the temporary would be lost when the temporary is destroyed.

continues
You can get around this by intoducing another non-temporary that is a reference.  The code is not guaranteed to work on all platforms/compilers, but it will work fine in VC (BC too).

int *&PPtr = *(int **) &p

PPtr++;

Let me know if you have questions.
By the way, it is a little clearer if you look at a cast as invoking a constructor function, which is really what it does and if you look at the operator as invoking a function, which again is really what it does.  So

((int* ) p)++

becomes

operator ++( int *(p) )

opperator ++ will require a non-constant reference  parameter, but the constructor for a temporary will return a constant value.
I may be wrong.  I think it is legal code an VC is in error.  According to the standard

1 The result of the expression (T) cast-expression is of  type  T.   The
  result  is an lvalue if T is a reference type, otherwise the result is
  an rvalue.  [Note: if T is a non-class type that is cv-qualified,  the
  cv-qualifiers  are  ignored when determining the type of the resulting
  rvalue; see _basic.lval_.  ]

I beleive that a pointer is considered a reference, so the result should be a lvalue.
From Steve Summit's C language FAQ:

4.5:      I have a char * pointer that happens to point to some ints, and I want to step it over them. Why doesn't ((int *)p)++;      work?

A: In C, a cast operator does not mean "pretend these bits have a different type, and treat them accordingly"; it is a conversion      operator, and by definition it yields an rvalue, which cannot be assigned to, or incremented with ++. (It is either an accident or a delibrate but nonstandard extension if a particular compiler accepts expressions such as the above.) Say what you mean: use
      p = (char *)((int *)p + 1);      

or (since p is a char *) simply            

p += sizeof(int);

Whenever possible, you should choose appropriate pointer types in the first place, instead of trying to treat one type as another.
That's what I thought--and originally said, but the standard does seem to say the result is an lvalue in this case....  maybe "reference type" only means references and not both pointers and references. Although, I don't think that is the case.
Avatar of NickRepin

ASKER

Thanks for you both, nietod and emild.

nietod, sorry, I have to reject your answer because it's 'It is impossible'. In my qestion, I said that I would not accept answers like 'It is impossible'.

Solution with reference actually is the same as just explicit conversion and assignment.

Strictly to say, I did not believe that I would get the positive answer on this Q because I already tried all to make it work. I though that may be I missed something.

The only reason I want to use the code above is that it is much more easy to type and, IMHO, it makes the program more clear (for me at least) because of less typing.

For example, we have function which converts wave samples to different formats. Samples can be 8 or 16 bits, stereo or mono. So we have 1 to 4 bytes per sample.

Convert(char* src,char* dst)
{
 for() {
  if(mono8) {
     char v=*src++;
     // convert
    *dst++=v;
  }
  else if(mono16) {
    SHORT v=*LPSHORT(src)++;
    // convert
    *LPSHORT(dst)++=v;

    //Instead of line above, I have to type
    *LPSHORT(dst)=v;
    dst+=sizeof(SHORT);

  }
  etc...
}

I know that it is non-standard in ANSI C, but it is the standard safe extension in Borland C++ and Visual C (it seems, not C++). I don't see any reason why not to make it work in C++.

<<When you cast the pointer to a new type a temporary pointer is (potentianly) created  (yes, its not actually necessary for this case, but this applies to any cast and there are casts where it would be necessary) >>

Yes, I know, but it's really not for this case.

By the way, Borland accepts, for example,
     (LPDWORD(p))[0]=0;

With VC, I have to type:

  ((LPDWORD)(p))[0]=0;

Two parentheses more...
 
Sorry, I have to correct the example above:

For Borland:
   (LPDWORD(p))[0]=0;
   (LPDWORD(p+1))[0]=0;
(the same style for both lines)

For Microsoft:
   ((LPDWORD)p)[0]=0;
   (LPDWORD(p+1))[0]=0;

(different styles).

Anyway, it is not so annoying as the problem described in my question above.
>> I have to reject your answer because it's 'It is impossible'.
But I gave you a work around.  you knew that what you were doing was not possible, you wanted another way to achieve the same goal, right?

>> Solution with reference actually is the same
The same in what sense?  The workaround produces the same behavior as your code was supposed to.  It does not employ an assignment, if that is important.  i.e you are manipulating the pointer directly through operations that assume it is a pointer of a different type.
Sorry, but my goal is to reduce typing overhead. I know all about workarounds.

My Q is about how to make ((int*)p)++ (exactly) work on VC++ compiler.
I already new that it doesn't work because I made some test, that's why I said that I would not accept 'negative' answer.

Solution with reference DOES employ assignment, int *&PPtr = *(int **) &p ,
but it doesn't matter. It has typing overhead even more than just p+=sizeof(int).

Sorry again, I understand that you spent some time typing comments here. But I tried to prevent wasting the your or anybody else time by adding the note about negative answer.



alright, but with those restrictions, i don't see how you expect to get an answer

the assignemt, by the way, is only a 1 time cost, the reference could the be manipulated repeatidly, which then changes the original pointer.  That is what I thought the goal was.  (and in fact the assignment and the reference itself are very likely to be optimized away, thus the solution should be completely  cost free, though not technically portable.)
Really I don't bother about performance at the moment. I just try to understand what MS VC can do and what cannot.

I have to clarify the problem. It is not as simple as it seems. Here is the link to MS doc about this:

http://msdn.microsoft.com/library/devprods/vs6/visualc/vccore/_core_microsoft_extensions_to_c.htm


Really I don't bother about performance at the moment. I just try to understand what MS VC can do and what cannot.

I have to clarify the problem. It is not as simple as it seems. Here is the link to MS doc about this:

http://msdn.microsoft.com/library/devprods/vs6/visualc/vccore/_core_microsoft_extensions_to_c.htm

Let's discuss use of non-ANSI casts to produce l-values, unnamed (anonymous) structures and unsized arrays as the last field in structures and unions.
According the link above, they all are so-called  " Microsoft extensions to the ANSI C standard". What exactly does it mean? Let's try to discover.
---------------
void a()  {
   char *p;
   (( int * ) p )++;
}

Trying to compile the code above as:
 cl  b.c   or  cl /Ze b.c -  works, C mode
cl /Za b.c  - doesn't work, C mode
cl  b.cpp, cl /Ze b.cpp, cl /Za b.cpp - doesn't work, C++ mode

Ok, so, may be, they are just extensions for C, not for C++? Let's try next example.
--------------
void a()
{
   union {
    int i;
    char *s;
   };
   s="aaa";
}

cl  b.c, cl /Ze b.c, cl /Za b.c - doesn't work, C mode
cl  b.cpp, cl /Ze b.cpp, cl /Za b.cpp - works, C++ mode

Doesn't work in C mode at all, works in C++ mode regardless of whether  extensions enabled or not.
------------
Let's try another example.
struct zero
{
    char *c;
    int zarray[];
};

cl  b.c,  cl /Ze b.c - works, C mode
cl /Za  b.c  - doesn't work, C mode

cl  b.cpp,  cl /Ze b.cpp - works, C++ mode
cl /Za  b.cpp  - doesn't work, C++ mode

That's good!
------------

So what does the all above mean? May be, some Microsoft programmer just mixed up different modes and made "cast-lvalue" feature in C, but not in C++? Or forgot to implement anonymous unions in C?
It seems that some "extensions" works in C mode only, some in C++ mode and some in both modes!

Here is yet another link.
http://msdn.microsoft.com/library/devprods/vs6/visualc/vccore/_core_disable_microsoft_extensions_to_c.2b2b.htm

It is titled " Disable Microsoft Extensions to C++" and contains: " To disable Microsoft extensions to C++ :",  " the compiler flags language constructs not compatible with ANSI C++ as errors".  

So, extensions are really for C++?!!

May be, I missed something? I hoped I did. That's why I asked this Q.


P.S. This is not the worst problem I met while moving from Borland to Microsoft compiler.

Microsoft *Visual* C++ *6.0*,  big company, billions dollars!
Now I understand why it is necessary to release, say, NT service packs 1,2,3,4,5,6....

Please correct me if I'm wrong.
Sorry, but that is just to tease you.  Those appear to be extensions for C not C++.  If you compile a program under C, then you can do

  (( int * ) p )++;

with the extensions enabled but not without the extensions enabled.

I see you alredy discovered that.  Strange the thing you discovered.  The intention of that docs is to document the C, not C++ extensions.  The VC help docs contain dectription of both sets of extensions, and that document explicitly says C not C++, so I think that it really means C.  Obviously there are some bugs in the compiler's handling of this and/or mistakes in the docs.

FYI You can turn the extensions on or off from the project settings, in the C/C++ tab, under "customize".   The switch appears for both C and C++ files, but has a different affect for each.   However you must have the extensions to compiler a windows program  (a program that includes windows.h.)
Great you found that too.  Now that I can explore links more easily, I shoud explore them all before answering.  Anyways what that doesn't indicate is that you can turn it on or off on a file-by-file basis and that it has a different effect for C and C++ files.

I really do think you will like VC over BC or BCB though.  VC's help is so much better, there is no comparison.  VC's debugger is far superior to.  Its main draw back is that it is so SLOW.  BCB compiles the identical code in about 1/10th the time.  Literally. But the code produced seems about equivalent in size and speed.  
How about a smart pointer:
class ref_pointer
{
   private:
      T*& ptr;
   public:
      template<class U>
      explicit ref_pointer(U*& u) : ptr((T*&)u){  }

      T& operator*() { return *ptr; }
      ref_pointer& operator++() { ++ptr; }
      // add more operators
};
ASKER CERTIFIED SOLUTION
Avatar of abk102299
abk102299

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
"with the extensions enabled but not without the extensions enabled."

"nietod" you're using a double negative there which means the same as the positive.  In effect, you're being repetitive.
If that's the worst of my mistakes, I'd be delighted.  But based on past experience, I assume there are about 100 spelling errors that I could not possibly fine, 50 typos that I could fine, but would commit another in the process of fixing, and 20 sentences in passive voice, that I could fix but would then sound strange to me.  I'll pass this on to the editor if if gets published, but I think that's unlikely.

>> a double negative there which means
>> the same as the positive
I may not be the best writter, but this is one fact should be as clear to a programmer as to writer.  Don't you think that is !false?
Saying something is "not false" is very clear; it means that it is "true".

Saying something is "true" and "not false" is being repetitive, ... just as "with the extensions enabled but not without the extensions enabled."

yeah...
((int*&)p)++;    
works fine, without any warnings.

But actually it is not the answer why ((int*)p)++ doesn't work in C++ mode.

<<I really do think you will like VC over BC or BCB>>

I have no choice. BC is no longer supported. I was disappointed with BCB when discovered that it doesn't contain such powerful project manager as BC one.
What's worse, in some circumstances Borland compilers produce twice slower code than Microsoft VC.
But in my opinion Borland compilers are really programmer-friendly with a lot of handy extensions and tools. For example, VC even version 6.0 has no utility to create import library from DLL. With Borland, it's as easy as IMPLIB A.LIB A.DLL.
Microsoft resource compiler doesn't support this:

#define VERSION "2.00"
STRINGTABLE {
 1,"AAA" "BBB"
 2,"Version"   VERSION
}

etc, etc, etc...
He he, in Dutch 'niet' means not :)

>> it doesn't contain such powerful project
>> manager as BC
I don't know why they did that.  And they claim you can write in MFC OWL, VCL or API, but really made a bunch of choices in the design that suggest they expect people to only use VCL.  They should not abandon BC if BCB is so biased toward VCL.

>> in some circumstances Borland compilers
>> produce twice slower code than Microsoft VC
I've not seen that.  In my experience the two have been quite comperable

>> VC even version 6.0 has no utility to
>> create import library from DLL.
I believe it does.  It comes with dozens of tools that are completely documented in the help.  Unfortunately, if you don't know name of the tool and what it does, you can't find the documentation.....  You might try asking on EE for the name of the VC utility or the technique by which it is done in VC.
Regarding speed. The problem with Borland compilers is that they don't inline memcpy etc except of strcpy only (inspite of documentation). 16-bit BC did, 32-bit BC and BCB don't.

Also they cannot inline functions which contain for, while, many ifs.

I wrote audio codec with several inline math functions contained for, while...
Resulting Borland's code was twice slower than Microsoft's one.

Regarding import library. I've seen article in MSDN library shipped with VC 6.0 that it doesn't contain such utility.
>> And they claim you can write in MFC OWL, VCL or API, but really made a bunch of choices in the design that suggest they expect people to only use VCL

erm.. can you use OWL or VCL in MSVC?
>> cannot inline functions which contain for, while, many ifs.
Ouch.  Maybe I can understand the loops--maybe, but ifs?  anyways I don't really see a difference, but I don't do much that is that CPU intensive.

>>can you use OWL or VCL in MSVC?
You probalby can use OWL as it doesn't require non-standard C++ compilation etc.  VCL requires some "extensions" to C++.  The frustrating thing is some of these extensions are still present/required in non VCL projects.  For example, a DLL must have its entry point function inside the main source code file for the DLL or it screws up the sort of "processing" that BCB does.  If you place the entry point in an include file or in a static library that gets linked in it does not work.  I think that might be acceptible for a VCL program, but not for a straight windows API one that doesn't use BCB "extras".