By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
435,561 Members | 3,265 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 435,561 IT Pros & Developers. It's quick & easy.

Does anyone know what this text means? (related to C4251 warning)

P: n/a
On MS site: http://msdn2.microsoft.com/en-us/lib...1w(VS.80).aspx

is the following garbled rambling:

"You can avoid exporting classes by defining a DLL that defines a class
with virtual functions, and functions you can call to instantiate and
delete objects of the type. You can then just call virtual functions on
the type."
Does anyone actually understand what is being said?. A simple example
would help.
Nov 1 '07 #1
Share this Question
Share on Google+
13 Replies


P: n/a
BTW David's way is what the article is referring to. Inheritance is done
via aggregation and implementing the same public interface. The actual
concrete class is not present in the public header file, only the pure
interface (contains only pure virtual functions) is. The factory functions
must be global to avoid name mangling issues, they would be prototyped in
the public header file. Destruction should be a Dispose function in the
v-table which calls "delete this;", avoiding the cast, or if reference
counting is used then AddRef/Release functions in the v-table.

Doug's way, although it might avoid the warning, does all the things the
warning is designed to discourage (exports class members, violates
one-definition rule, etc).
Nov 1 '07 #2

P: n/a


Ben Voigt [C++ MVP] wrote:
BTW David's way is what the article is referring to. Inheritance is done
via aggregation and implementing the same public interface. The actual
concrete class is not present in the public header file, only the pure
interface (contains only pure virtual functions) is. The factory functions
must be global to avoid name mangling issues, they would be prototyped in
the public header file. Destruction should be a Dispose function in the
v-table which calls "delete this;", avoiding the cast, or if reference
counting is used then AddRef/Release functions in the v-table.

Doug's way, although it might avoid the warning, does all the things the
warning is designed to discourage (exports class members, violates
one-definition rule, etc).


Just to make sure we are 'on the same page':

From what you say above, David's example header should include lines
somewhat like this:

// Header
IBase* DECLSPEC_MACRO CreateClass() ;
void DECLSPEC_MACRO DestroyClass(IBase* pBase) ;

Where DECLSPEC_MACRO simply evaluates to a
__declspec(dllimport/dllexport) as the case may be

Is my understanding correct ?

Also, could you please explain this paragraph in your response:

"Destruction should be a Dispose function in the v-table which calls
"delete this;"

Surely, if the Construction/Destruction are global functions, then they
can't have a 'this' pointer?

Dis you make a mistake, or am I missing something ?
Nov 1 '07 #3

P: n/a

"Anonymous" <no******@here.comwrote in message
news:MY******************************@bt.com...
>

Ben Voigt [C++ MVP] wrote:
>BTW David's way is what the article is referring to. Inheritance is done
via aggregation and implementing the same public interface. The actual
concrete class is not present in the public header file, only the pure
interface (contains only pure virtual functions) is. The factory
functions must be global to avoid name mangling issues, they would be
prototyped in the public header file. Destruction should be a Dispose
function in the v-table which calls "delete this;", avoiding the cast, or
if reference counting is used then AddRef/Release functions in the
v-table.

Doug's way, although it might avoid the warning, does all the things the
warning is designed to discourage (exports class members, violates
one-definition rule, etc).


Just to make sure we are 'on the same page':

From what you say above, David's example header should include lines
somewhat like this:

// Header
IBase* DECLSPEC_MACRO CreateClass() ;
void DECLSPEC_MACRO DestroyClass(IBase* pBase) ;
Pretty much, unless you use self-destruction.

Also, it's common to use an out parameter instead of a return value for the
new object, like:

HRESULT DECLSPEC_MACRO CreateClass(IBase** pRetval);
>
Where DECLSPEC_MACRO simply evaluates to a __declspec(dllimport/dllexport)
as the case may be
and MY_LIBRARY_DLL is a better name than DECLSPEC_MACRO
>
Is my understanding correct ?

Also, could you please explain this paragraph in your response:

"Destruction should be a Dispose function in the v-table which calls
"delete this;"

Surely, if the Construction/Destruction are global functions, then they
can't have a 'this' pointer?

Dis you make a mistake, or am I missing something ?
This would be a virtual member function Dispose instead of a global
destruction function, because the global destruction function has to do a
cast, whereas a member function is pretty well assured that the object is
the correct type.
Nov 1 '07 #4

P: n/a

"Doug Harrison [MVP]" <ds*@mvps.orgwrote in message
news:pb********************************@4ax.com...
On Thu, 1 Nov 2007 08:38:35 -0500, "Ben Voigt [C++ MVP]"
<rb*@nospam.nospamwrote:
>>BTW David's way is what the article is referring to. Inheritance is done
via aggregation and implementing the same public interface.

That's not "inheritance" in the C++ sense of the word.
It isn't type derivation, but it is inheritance in the OOP sense of the
word.

[snip]
>>Doug's way, although it might avoid the warning, does all the things the
warning is designed to discourage (exports class members, violates
one-definition rule, etc).

It doesn't just "avoid the warning". What I talked about in the last
paragraph of my message lets you use the class as a real C++ class,
instead
of some clumsy COM simulation. Not everything is a nail.
That's misleading. What you could say is "it's a real Visual C++ 8.0 class
dynamically linking the debug runtime library, with 4-byte packing, and
STL_CHECKED_ITERATORS=0"... if those are in fact the compiler options used.
Whereas the source code for a real C++ class can be used in any C++ program
and compiled with any C++ standard-compliant compiler.

[snip]
>
Where did what I posted violate the ODR, BTW?
The definition of class X is different between the provider and consumer.

Every single use of dllimport/dllexport on a type violates the ODR.
Sometimes you can get away with it, sometimes not.
>
--
Doug Harrison
Visual C++ MVP

Nov 1 '07 #5

P: n/a


Ben Voigt [C++ MVP] wrote:
"Anonymous" <no******@here.comwrote in message
news:MY******************************@bt.com...
>>
Ben Voigt [C++ MVP] wrote:
>>>BTW David's way is what the article is referring to. Inheritance is done
via aggregation and implementing the same public interface. The actual
concrete class is not present in the public header file, only the pure
interface (contains only pure virtual functions) is. The factory
functions must be global to avoid name mangling issues, they would be
prototyped in the public header file. Destruction should be a Dispose
function in the v-table which calls "delete this;", avoiding the cast, or
if reference counting is used then AddRef/Release functions in the
v-table.

Doug's way, although it might avoid the warning, does all the things the
warning is designed to discourage (exports class members, violates
one-definition rule, etc).


Just to make sure we are 'on the same page':

From what you say above, David's example header should include lines
somewhat like this:

// Header
IBase* DECLSPEC_MACRO CreateClass() ;
void DECLSPEC_MACRO DestroyClass(IBase* pBase) ;


Pretty much, unless you use self-destruction.

Also, it's common to use an out parameter instead of a return value for the
new object, like:

HRESULT DECLSPEC_MACRO CreateClass(IBase** pRetval);

>>Where DECLSPEC_MACRO simply evaluates to a __declspec(dllimport/dllexport)
as the case may be


and MY_LIBRARY_DLL is a better name than DECLSPEC_MACRO

>>Is my understanding correct ?

Also, could you please explain this paragraph in your response:

"Destruction should be a Dispose function in the v-table which calls
"delete this;"

Surely, if the Construction/Destruction are global functions, then they
can't have a 'this' pointer?

Dis you make a mistake, or am I missing something ?


This would be a virtual member function Dispose instead of a global
destruction function, because the global destruction function has to do a
cast, whereas a member function is pretty well assured that the object is
the correct type.

All this seems unnecessarily complicated - it sounds like reinventing
the (COM) wheel. Maybe I should just use the PImpl Idiom (typesafe
opaque ptr) to move all of the private data from the header file into
the implementation - that will certainly make the compiler shut up about
C4251 warnings ?
Nov 2 '07 #6

P: n/a
On Fri, 02 Nov 2007 00:34:23 +0000, Anonymous <no******@here.comwrote:
>All this seems unnecessarily complicated - it sounds like reinventing
the (COM) wheel.
To a large extent, that's exactly what it is.
>Maybe I should just use the PImpl Idiom (typesafe
opaque ptr) to move all of the private data from the header file into
the implementation - that will certainly make the compiler shut up about
C4251 warnings ?
Yes, using the PImpl idiom will avoid C4251, and so will #pragma
warning(disable). For the most part, this warning can be ignored. It's an
issue primarily when class templates that have static data are involved.[*]
See my reply to Ben for more on that. Also, when exporting whole classes,
you must do as I said in my first reply to you, namely, "link everyone to
the same CRT DLL and consider this usage equivalent to static linking WRT
compilation dependencies."
[*] Another way C4251 can be legitimate is if an inline function in a
dllexported class calls a function in a non-exported class. ISTR that VC6
would inline those functions even though their class is exported, and that
would lead to a linker error, because the non-exported function couldn't be
found. VC2005 does not appear to inline such functions, so that leaves
template static data as the only real issue.

--
Doug Harrison
Visual C++ MVP
Nov 2 '07 #7

P: n/a
On Thu, 1 Nov 2007 17:45:41 -0500, "Ben Voigt [C++ MVP]"
<rb*@nospam.nospamwrote:
>Where did what I posted violate the ODR, BTW?

The definition of class X is different between the provider and consumer.

Every single use of dllimport/dllexport on a type violates the ODR.
Can you write a program that can detect it and cares about it? Since "every
single use violates the rule", please restrict yourself to the simplest
type imaginable. :)

Listen, when you use __declspec(dllexport|dllimport), you're no longer
writing in Standard C++. Someone who wants to use the feature doesn't care
about inconsequential violations of the ODR; he cares only that the program
behaves as he expects a C++ program to behave. I'm happy to talk about
relevant ways in which expectations are violated, because they're important
for people using the feature to understand.

--
Doug Harrison
Visual C++ MVP
Nov 2 '07 #8

P: n/a

"Doug Harrison [MVP]" <ds*@mvps.orgwrote in message
news:38********************************@4ax.com...
On Thu, 1 Nov 2007 17:45:41 -0500, "Ben Voigt [C++ MVP]"
<rb*@nospam.nospamwrote:
>>Where did what I posted violate the ODR, BTW?

The definition of class X is different between the provider and consumer.

Every single use of dllimport/dllexport on a type violates the ODR.

Can you write a program that can detect it and cares about it? Since
"every
single use violates the rule", please restrict yourself to the simplest
type imaginable. :)
#if BAD_DLL_BUILD
#define BAD_DLL __declspec(dllexport)
#else
#define BAD_DLL __declspec(dllimport)
#endif

BAD_DLL class Badness
{
char first;
double second;
public:
double getValue() { return second; }
};

First up, the ODR is violated because BAD_DLL is defined differently in each
compilation unit.

Practically, because getValue is defined inside the class declaration, it is
automatically marked for inlining. Therefore each compile unit will
independently calculate the class layout. Since the class layout may not be
the same for the DLL and EXE (only the v-table layout is part of the ABI and
guaranteed to remain the same), there's a problem.
>
Listen, when you use __declspec(dllexport|dllimport), you're no longer
writing in Standard C++. Someone who wants to use the feature doesn't care
about inconsequential violations of the ODR; he cares only that the
program
behaves as he expects a C++ program to behave. I'm happy to talk about
relevant ways in which expectations are violated, because they're
important
for people using the feature to understand.

--
Doug Harrison
Visual C++ MVP

Nov 6 '07 #9

P: n/a
On Tue, 6 Nov 2007 15:17:20 -0600, "Ben Voigt [C++ MVP]"
<rb*@nospam.nospamwrote:
>#if BAD_DLL_BUILD
#define BAD_DLL __declspec(dllexport)
#else
#define BAD_DLL __declspec(dllimport)
#endif

BAD_DLL class Badness
{
char first;
double second;
public:
double getValue() { return second; }
};

First up, the ODR is violated because BAD_DLL is defined differently in each
compilation unit.
I'll say it again: Listen, when you use __declspec(dllexport|dllimport),
you're no longer writing in Standard C++. Someone who wants to use the
feature doesn't care about inconsequential violations of the ODR; he cares
only that the program behaves as he expects a C++ program to behave. I'm
happy to talk about relevant ways in which expectations are violated,
because they're important for people using the feature to understand.

(Key terms are "inconsequential violations" and "relevant ways". You are at
best talking about the former, and in no way, the latter.)
>Practically, because getValue is defined inside the class declaration, it is
automatically marked for inlining. Therefore each compile unit will
independently calculate the class layout. Since the class layout may not be
the same for the DLL and EXE (only the v-table layout is part of the ABI and
guaranteed to remain the same), there's a problem.
I'll ask you again: Can you write a program that can detect it and cares
about it? ("It" being this purported ODR violation.) In case you don't
realize it, you haven't demonstrated anything; you've just made some new
unsubstantiated claims.

NB: You'll discover your glaringly obvious syntax error when you try to
write this program. Anybody bold enough to categorically proclaim "BAD_DLL"
ought to be able to write this program and demonstrate that there is an
actual problem. It's not a lot to ask. However, I hope you realize anyone
who understands the feature can write "GOOD_DLL"; indeed, MS uses
__declspec(dllexport|dllimport) in the C and C++ DLLs to export the common
basic_string specializations, std::exception, and various other classes. So
even if you are able to contrive a "BAD_DLL", I expect to find you've used
the feature incorrectly.

--
Doug Harrison
Visual C++ MVP
Nov 7 '07 #10

P: n/a

"Doug Harrison [MVP]" <ds*@mvps.orgwrote in message
news:i5********************************@4ax.com...
On Tue, 6 Nov 2007 15:17:20 -0600, "Ben Voigt [C++ MVP]"
<rb*@nospam.nospamwrote:
>>#if BAD_DLL_BUILD
#define BAD_DLL __declspec(dllexport)
#else
#define BAD_DLL __declspec(dllimport)
#endif

BAD_DLL class Badness
{
char first;
double second;
public:
double getValue() { return second; }
};

First up, the ODR is violated because BAD_DLL is defined differently in
each
compilation unit.

I'll say it again: Listen, when you use __declspec(dllexport|dllimport),
you're no longer writing in Standard C++. Someone who wants to use the
feature doesn't care about inconsequential violations of the ODR; he cares
only that the program behaves as he expects a C++ program to behave. I'm
happy to talk about relevant ways in which expectations are violated,
because they're important for people using the feature to understand.

(Key terms are "inconsequential violations" and "relevant ways". You are
at
best talking about the former, and in no way, the latter.)
>>Practically, because getValue is defined inside the class declaration, it
is
automatically marked for inlining. Therefore each compile unit will
independently calculate the class layout. Since the class layout may not
be
the same for the DLL and EXE (only the v-table layout is part of the ABI
and
guaranteed to remain the same), there's a problem.

I'll ask you again: Can you write a program that can detect it and cares
about it? ("It" being this purported ODR violation.) In case you don't
realize it, you haven't demonstrated anything; you've just made some new
unsubstantiated claims.
Yes, using pragma pack or any of a number of other options that affect the
class layout but work correctly when used consistently within a DLL, except
for dllexport types. As I said, only the v-table layout is part of the ABI
and is guaranteed to be the same in different DLLs.
>
NB: You'll discover your glaringly obvious syntax error when you try to
write this program. Anybody bold enough to categorically proclaim
"BAD_DLL"
ought to be able to write this program and demonstrate that there is an
actual problem. It's not a lot to ask. However, I hope you realize anyone
who understands the feature can write "GOOD_DLL"; indeed, MS uses
__declspec(dllexport|dllimport) in the C and C++ DLLs to export the common
basic_string specializations, std::exception, and various other classes.
So
even if you are able to contrive a "BAD_DLL", I expect to find you've used
the feature incorrectly.
I see I caused confusion by naming my library "Ben's Awkward Demonstration"
because the acronym looks like the word bad.

Anyway, Microsoft using dllexport on types in the standard library causes no
end of problems, which frequently appear in this newsgroup.
>
--
Doug Harrison
Visual C++ MVP

Nov 7 '07 #11

P: n/a
On Wed, 7 Nov 2007 08:22:33 -0600, "Ben Voigt [C++ MVP]"
<rb*@nospam.nospamwrote:
>Yes, using pragma pack or any of a number of other options that affect the
class layout but work correctly when used consistently within a DLL, except
for dllexport types.
What do you mean, "except for dllexport types"?

If you use #pragma pack inconsistently on types for which it matters, you
are causing undefined behavior:

1. You can easily observe this within a single module by using different
packing in two translation units for a normal, non-dllexport type.

2. You can easily observe this for non-dllexport types shared between
modules. For example, there were (once) bugs due to inconsistent packing
affecting Windows's <winsock2.hheader and KEY_EVENT_RECORD type.

I hope you can see that problems like this exist independently of
dllexport.
>I see I caused confusion by naming my library "Ben's Awkward Demonstration"
because the acronym looks like the word bad.
The problem is, you didn't "demonstrate" anything.
>Anyway, Microsoft using dllexport on types in the standard library causes no
end of problems, which frequently appear in this newsgroup.
Then you should be able to provide numerous examples of "Microsoft using
dllexport on types in the standard library causing no end of problems".

--
Doug Harrison
Visual C++ MVP
Nov 7 '07 #12

P: n/a

"Doug Harrison [MVP]" <ds*@mvps.orgwrote in message
news:58********************************@4ax.com...
On Wed, 7 Nov 2007 08:22:33 -0600, "Ben Voigt [C++ MVP]"
<rb*@nospam.nospamwrote:
>>Yes, using pragma pack or any of a number of other options that affect the
class layout but work correctly when used consistently within a DLL,
except
for dllexport types.

What do you mean, "except for dllexport types"?

If you use #pragma pack inconsistently on types for which it matters, you
are causing undefined behavior:

1. You can easily observe this within a single module by using different
packing in two translation units for a normal, non-dllexport type.
True, but this can be (and in the newest versions of VC++ it usually is)
caught by the linker.

When you add dllexport to the mix, the difficult of matching settings
increases exponentially because you now have to match multiple binaries from
multiple vendors...
>
2. You can easily observe this for non-dllexport types shared between
modules. For example, there were (once) bugs due to inconsistent packing
affecting Windows's <winsock2.hheader and KEY_EVENT_RECORD type.

I hope you can see that problems like this exist independently of
dllexport.
>>I see I caused confusion by naming my library "Ben's Awkward
Demonstration"
because the acronym looks like the word bad.

The problem is, you didn't "demonstrate" anything.
>>Anyway, Microsoft using dllexport on types in the standard library causes
no
end of problems, which frequently appear in this newsgroup.

Then you should be able to provide numerous examples of "Microsoft using
dllexport on types in the standard library causing no end of problems".
Specifically, people using binary third-party libraries that use STL objects
in the public API, that then prevent the consumer (programmer, not end-user)
from upgrading to a new compiler.

People wanting/needing to use two such libraries, that haven't used
identical settings to each other.
>
--
Doug Harrison
Visual C++ MVP

Nov 13 '07 #13

P: n/a
I was really hoping you would produce several links. Then I would have
shown that there is a single answer to your "no end of problems," namely,
what I've been saying for years (not to mention in my first post in this
thread) about considering this use of DLLs equivalent to static linking
for
compilation dependency purposes.
I agree with that, except that "for compilation dependency purposes"
confuses the issue without adding any information IMHO.

However, that adds a lot of limitations, and avoiding those limitations was
the whole point of the text the OP quoted and asked about. Again, in my
opinion the language you used suggested that dllexport was just as good or
better than the method suggested by the MSDN article, and this just isn't
true in many (possibly most) situations.

The real question is "what value does dllexport have over static linking"
and aside from staggered loading, I can't think of anything. I'm pretty
sure it has no value whatsoever to most projects.
>
--
Doug Harrison
Visual C++ MVP

Nov 14 '07 #14

This discussion thread is closed

Replies have been disabled for this discussion.