Archivos de encabezado de redefinición de C ++ (winsock2.h)

143

¿Cómo evito incluir archivos de encabezado dos veces? El problema es que incluyo elen MyClass.h y luego incluyo MyClass.h en muchos archivos, por lo que incluye varias veces y se produce un error de redefinición. ¿Como prevenir?

Estoy usando #pragma una vez en lugar de incluir guardias, y supongo que está bien.

MyClass.h:

// MyClass.h
#pragma once

#include <winsock2.h>

class MyClass
{

// methods
public:
 MyClass(unsigned short port);
 virtual ~MyClass(void);
};

EDITAR: Pocos de los errores que estoy recibiendo

c:\program files\microsoft sdks\windows\v6.0a\include\ws2def.h(91) : warning C4005: 'AF_IPX' : macro redefinition
        c:\program files\microsoft sdks\windows\v6.0a\include\winsock.h(460) : see previous definition of 'AF_IPX'
c:\program files\microsoft sdks\windows\v6.0a\include\ws2def.h(124) : warning C4005: 'AF_MAX' : macro redefinition
        c:\program files\microsoft sdks\windows\v6.0a\include\winsock.h(479) : see previous definition of 'AF_MAX'
c:\program files\microsoft sdks\windows\v6.0a\include\ws2def.h(163) : warning C4005: 'SO_DONTLINGER' : macro redefinition
        c:\program files\microsoft sdks\windows\v6.0a\include\winsock.h(402) : see previous definition of 'SO_DONTLINGER'
c:\program files\microsoft sdks\windows\v6.0a\include\ws2def.h(206) : error C2011: 'sockaddr' : 'struct' type redefinition
        c:\program files\microsoft sdks\windows\v6.0a\include\winsock.h(485) : see declaration of 'sockaddr'
c:\program files\microsoft sdks\windows\v6.0a\include\ws2def.h(384) : error C2143: syntax error : missing '}' before 'constant'
c:\program files\microsoft sdks\windows\v6.0a\include\ws2def.h(384) : error C2143: syntax error : missing ';' before 'constant'
c:\program files\microsoft sdks\windows\v6.0a\include\ws2def.h(384) : error C2059: syntax error : 'constant'
c:\program files\microsoft sdks\windows\v6.0a\include\ws2def.h(437) : error C2143: syntax error : missing ';' before '}'
c:\program files\microsoft sdks\windows\v6.0a\include\ws2def.h(437) : error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
c:\program files\microsoft sdks\windows\v6.0a\include\ws2def.h(437) : error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
c:\program files\microsoft sdks\windows\v6.0a\include\ws2def.h(518) : warning C4005: 'IN_CLASSA' : macro redefinition
        c:\program files\microsoft sdks\windows\v6.0a\include\winsock.h(287) : see previous definition of 'IN_CLASSA'
c:\program files\microsoft sdks\windows\v6.0a\include\ws2def.h(524) : warning C4005: 'IN_CLASSB' : macro redefinition
        c:\program files\microsoft sdks\windows\v6.0a\include\winsock.h(293) : see previous definition of 'IN_CLASSB'
c:\program files\microsoft sdks\windows\v6.0a\include\ws2def.h(530) : warning C4005: 'IN_CLASSC' : macro redefinition
        c:\program files\microsoft sdks\windows\v6.0a\include\winsock.h(299) : see previous definition of 'IN_CLASSC'
c:\program files\microsoft sdks\windows\v6.0a\include\ws2def.h(541) : warning C4005: 'INADDR_ANY' : macro redefinition
        c:\program files\microsoft sdks\windows\v6.0a\include\winsock.h(304) : see previous definition of 'INADDR_ANY'
c:\program files\microsoft sdks\windows\v6.0a\include\ws2def.h(543) : warning C4005: 'INADDR_BROADCAST' : macro redefinition
        c:\program files\microsoft sdks\windows\v6.0a\include\winsock.h(306) : see previous definition of 'INADDR_BROADCAST'
c:\program files\microsoft sdks\windows\v6.0a\include\ws2def.h(577) : error C2011: 'sockaddr_in' : 'struct' type redefinition
        c:\program files\microsoft sdks\windows\v6.0a\include\winsock.h(312) : see declaration of 'sockaddr_in'
c:\program files\microsoft sdks\windows\v6.0a\include\winsock2.h(132) : error C2011: 'fd_set' : 'struct' type redefinition
        c:\program files\microsoft sdks\windows\v6.0a\include\winsock.h(68) : see declaration of 'fd_set'
c:\program files\microsoft sdks\windows\v6.0a\include\winsock2.h(167) : warning C4005: 'FD_SET' : macro redefinition
        c:\program files\microsoft sdks\windows\v6.0a\include\winsock.h(102) : see previous definition of 'FD_SET'
c:\program files\microsoft sdks\windows\v6.0a\include\winsock2.h(176) : error C2011: 'timeval' : 'struct' type redefinition
        c:\program files\microsoft sdks\windows\v6.0a\include\winsock.h(111) : see declaration of 'timeval'
c:\program files\microsoft sdks\windows\v6.0a\include\winsock2.h(232) : error C2011: 'hostent' : 'struct' type redefinition
        c:\program files\microsoft sdks\windows\v6.0a\include\winsock.h(167) : see declaration of 'hostent'
c:\program files\microsoft sdks\windows\v6.0a\include\winsock2.h(245) : error C2011: 'netent' : 'struct' type redefinition
        c:\program files\microsoft sdks\windows\v6.0a\include\winsock.h(180) : see declaration of 'netent'
c:\program files\microsoft sdks\windows\v6.0a\include\winsock2.h(252) : error C2011: 'servent' : 'struct' type redefinition
        c:\program files\microsoft sdks\windows\v6.0a\include\winsock.h(187) : see declaration of 'servent'
c:\program files\microsoft sdks\windows\v6.0a\include\winsock2.h(264) : error C2011: 'protoent' : 'struct' type redefinition
        c:\program files\microsoft sdks\windows\v6.0a\include\winsock.h(199) : see declaration of 'protoent'
akif
fuente
44
Ya está usando #pragma una vez, por lo que debe incluirse solo una vez.
Naveen
1
¿Tu compilador no admite pragma una vez?
Svetlozar Angelov
Estoy usando Visual Studio 2008, ¿por qué se incluye <winsock2.h> dos veces?
akif
1
Podría incluirse dos veces desde algunos de los encabezados incluidos de MyClass.h
Svetlozar Angelov
55
winsock2 y winsock tienen estructuras comunes.
Debe

Respuestas:

234

Este problema es causado cuando se incluye <windows.h>antes <winsock2.h>. Intente organizar su lista de inclusión que <windows.h>se incluye después <winsock2.h>o defina _WINSOCKAPI_primero:

#define _WINSOCKAPI_    // stops windows.h including winsock.h
#include <windows.h>
// ...
#include "MyClass.h"    // Which includes <winsock2.h>

Ver también esto .

pingw33n
fuente
No estoy incluyendo <windows.h> en absoluto, sé que <winsock2.h> lo hace por mí.
akif
2
Para mí, su código se compila bien solo <winsock2.h>en MSVC2008. <windows.h>la inclusión hace que genere errores de compilación idénticos a los que usted proporcionó.
pingw33n
¿Se incluye <windows.h> en stdafx.h?
Colin Desmond el
1
Esta solución me solucionó el problema en VS 2010 con SDK 7.1. Gracias pingw33n!
adamfisk
Tenía #include <winsock2.h> #include <ws2tcpip.h> #include <windows.h>en orden y estaba obteniendo el archivo winsock2, h no encontrado. Incluido #define _WINSOCKAPI_ por encima de los 3 incluye aún el mismo error
Ava
75

Como otros sugirieron, el problema es cuando windows.hse incluye antes WinSock2.h. Porque windows.hincluye winsock.h. No puedes usar ambos WinSock2.hy winsock.h.

Soluciones:

  • Incluir WinSock2.hantes windows.h. En el caso de encabezados precompilados, debe resolverlo allí. En el caso de un proyecto simple, es fácil. Sin embargo, en grandes proyectos (especialmente cuando se escribe código portátil, sin encabezados precompilados) puede ser muy difícil, porque cuando WinSock2.hse incluye su encabezado con , windows.hya se puede incluir desde algún otro archivo de encabezado / implementación.

  • Definir WIN32_LEAN_AND_MEANantes windows.ho proyectar todo. Pero excluirá muchas otras cosas que pueda necesitar y debe incluirlas usted mismo.

  • Definir _WINSOCKAPI_antes windows.ho proyectar todo. Pero cuando lo incluye WinSock2.h, obtiene una advertencia de redefinición de macros.

  • Use en windows.hlugar de WinSock2.hcuándo winsock.hes suficiente para su proyecto (en la mayoría de los casos lo es). Esto probablemente dará como resultado un tiempo de compilación más largo, pero resuelve cualquier error / advertencia.

Pavel Machyniak
fuente
14
WIN32_LEAN_AND_MEANera la solución para los tanques de mí mucho
Jonatan Cloutier
Acerca de la _WINSOCK_solución: no debe obtener una advertencia de redefinición de macros si ambas definiciones son idénticas. El error común es que las personas agregan definición al proyecto sin establecer ningún valor y esperan una definición vacía. Sin embargo, si agrega -D_WINSOCK_a la línea cmd, se establecerá _WINSOCK_en 1. Para crear una definición vacía, se -D_WINSOCK_=debe pasar.
Paweł Stankowski
Si usa #define _WINSOCKAPI_, también puede necesitar #define _WINSOCK_DEPRECATED_NO_WARNINGS, dependiendo de sus circunstancias.
Lorien Brune
16

Oh, la fealdad de Windows ... El orden de inclusión es importante aquí. Debe incluir winsock2.h antes de windows.h. Dado que windows.h probablemente se incluye desde su encabezado precompilado (stdafx.h), deberá incluir winsock2.h desde allí:

#include <winsock2.h>
#include <windows.h>
Daniel Paull
fuente
14

Mediante el uso de "protectores de encabezado":

#ifndef MYCLASS_H
#define MYCLASS_H

// This is unnecessary, see comments.
//#pragma once

// MyClass.h

#include <winsock2.h>

class MyClass
{

// methods
public:
    MyClass(unsigned short port);
    virtual ~MyClass(void);
};

#endif
DevSolar
fuente
2
Supongo que estoy equivocado (4 votos a favor por ahora), pero creo que usar incluir guardias es lo mismo que pragma una vez, ¿los pones a ambos?
Svetlozar Angelov
1
Bueno, tengo #pragma una vez que afaik es el mismo encabezado
akif
2
@Angelov: Sí, eso es lo que digo que son las mismas cosas. El problema no está en mis archivos de encabezado, pero creo que <winsock2.h> en sí mismo no tiene protectores de encabezado o puede ser algo más.
akif
1
Por definición, #pragma depende del compilador (no estándar). Es posible que no funcione en todos los compiladores. Sé que Visual Studio acepta #pargma una vez. No estoy seguro si gcc lo hace. Sé que incluir guardias SIEMPRE funciona. Uso ambos #pragma una vez e incluyo guardias para una máxima capacidad de calificación. Parece que MSVC ha optimizado el manejo de #pragma una vez y gcc ha optimizado el manejo de incluir guardias. La única diferencia con mi encabezado estándar es que #praga una vez está fuera de los guardias de inclusión.
KitsuneYMG
1
El comando '#pragma' se especifica en el estándar ANSI para tener un efecto arbitrario definido por la implementación. En el preprocesador GNU C, '#pragma' primero intenta ejecutar el juego 'pícaro'; si eso falla, intenta ejecutar el juego 'hackear'; si eso falla, intenta ejecutar GNU Emacs mostrando la Torre de Hanoi; si eso falla, informa un error fatal. En cualquier caso, el preprocesamiento no continúa. - Richard M. Stallman, The GNU C Preprocessor, versión 1.34
DevSolar
6

Me encontré con este problema al intentar extraer un paquete de terceros que aparentemente incluía windows.h en algún lugar de su lío de encabezados. Definir _WINSOCKAPI_a nivel de proyecto fue mucho más fácil (sin mencionar que es más fácil de mantener) que vadear su sopa y arreglar la inclusión problemática.

Yaur
fuente
1
En Qt, en el archivo .pro, se ve así: DEFINES += _WINSOCKAPI_
phyatt
@phyatt: deberías convertir eso en una respuesta, si no lo haces ¡lo haré!
Leif Gruenwoldt
@LeifGruenwoldt, ¡adelante! Me alegro de poder ayudar.
phyatt
6

En VS 2015, lo siguiente funcionará:

#define _WINSOCKAPI_

Si bien lo siguiente no lo hará:

#define WIN32_LEAN_AND_MEAN
MariuszW
fuente
6

Comprobé el recursiva incluye, vi a los ficheros de cabecera que incluyen (de forma recursiva) alguna #include "windows.h"y #include "Winsock.h"y escribir una #include "Winsock2.h". en estos archivos, agregué #include "Winsock2.h"como la primera inclusión.

Solo es cuestión de paciencia, mira incluye uno por uno y establece este orden, primero #include "Winsock2.h"luego#include "windows.h"

Kiriloff
fuente
5

Encontré este enlace windows.h y winsock2.h que tiene una alternativa que funcionó muy bien para mí:

#define _WINSOCKAPI_    // stops windows.h including winsock.h
#include <windows.h>
#include <winsock2.h>

Estaba teniendo problemas para encontrar dónde ocurrió el problema, pero al agregar que #define pude construir sin descubrirlo.

Benjamin Herreid
fuente
4

No usaría solo FILENAME_H pero

#ifndef FILENAME_H_AF06570D_B36E_4B82_8F97_C456AF4A38FD
#define FILENAME_H_AF06570D_B36E_4B82_8F97_C456AF4A38FD

//code stuff
#endif // FILENAME_H_AF06570D_B36E_4B82_8F97_C456AF4A38FD

Siempre he usado un postfix guid. Me encontré con una base de código muy pobre hace algunos años que tenía diferentes archivos de encabezado con el mismo nombre de archivo e incluye guardia. Los archivos en cuestión habían definido una clase con el mismo nombre. Si solo se usaran espacios de nombres. Algunos proyectos compilados otros no. El uso de guardias únicos era parte de la solución para diferenciar los encabezados y sus contenidos.

En Windows con Visual Studio, use guidgen.exe, en Linux uuidgen -t.

Sam
fuente
4

Me he encontrado con el mismo problema y esto es lo que he descubierto hasta ahora:

De este fragmento de salida:

c: \ archivos de programa \ microsoft sdks \ windows \ v6.0a \ include \ ws2def.h (91): advertencia C4005: 'AF_IPX': redefinición de macros
c: \ archivos de programa \ microsd sdks \ windows \ v6.0a \ include \ winsock.h (460): consulte la definición anterior de 'AF_IPX'

-Parece que tanto ws2def.h como winsock.h se han incluido en su solución.

Si observa el archivo ws2def.h, comienza con el siguiente comentario:

/*++

Copyright (c) Microsoft Corporation. All rights reserved.

Module Name:

    ws2def.h

Abstract:

    This file contains the core definitions for the Winsock2
    specification that can be used by both user-mode and 
    kernel mode modules.

    This file is included in WINSOCK2.H. User mode applications
    should include WINSOCK2.H rather than including this file
    directly. This file can not be included by a module that also
    includes WINSOCK.H.

Environment:

    user mode or kernel mode

--*/

Preste atención a la última línea: "Este archivo no puede ser incluido por un módulo que también incluya WINSOCK.H"

Todavía trato de corregir el problema sin hacer cambios en el código.

Avísame si esto tiene sentido.

Shailesh Tainwala
fuente
2

Deberías usar el protector de cabecera.

poner esas líneas en la parte superior del archivo de encabezado

#ifndef PATH_FILENAME_H
#define PATH_FILENAME_H

y en la parte inferior

#endif
ntcong
fuente
1
#pragma una vez e incluir guardias son las mismas cosas ¿no?
akif
No son exactamente lo mismo: los protectores de encabezado evitarán la reincorporación del archivo en el nivel de preprocesador, además, obviamente, son un poco más portátiles que #pragma una vez.
Timo Geusch
1
Quise
2
#pragma una vez no es estándar, afaik
ntcong
2

#pragma oncese basa en la ruta completa del nombre del archivo. Entonces, lo que probablemente tenga es que hay dos copias idénticas de MyClass.h o Winsock2.h en diferentes directorios.

soru
fuente
Un enlace simbólico o unión NTFS también hará que el sistema se rompa.
Thomi
1

#pragma oncees flakey, incluso en compiladores de MS, y no es compatible con muchos otros compiladores. Como muchas otras personas han mencionado, usar incluir guardias es el camino a seguir. No lo uses #pragma onceen absoluto: te hará la vida mucho más fácil.

Thomi
fuente
3
Desafortunadamente, he visto más de cero protectores fallidos, ya sea donde un error tipográfico significa que el protector realmente no funciona, o donde los archivos del mismo nombre en diferentes directorios usan el mismo token, o donde el token usado comienza con un doble guión bajo o guión bajo, luego mayúscula (y, por lo tanto, no es portátil, como #pragma una vez). Entonces, para el código inherentemente no portátil, como cualquier cosa que use winsock.h, #pragma me molestó profundamente una vez hasta el punto en que dijiste que era inestable. ¿Cuándo falla, aparte de no ser compatible?
Steve Jessop
3
Cuando se usa #pragma once, el compilador toma el nombre del nodo del archivo de encabezado como ID único. Esto puede fallar si tiene enlaces simbólicos o uniones NTFS en su árbol de origen (más común de lo que podría pensar), o incluso si tiene un archivo del mismo nombre en otro directorio de inclusión del sistema (esto me ha sucedido antes cuando tenía la versión 1 y la versión 2 de la misma biblioteca instalada en dos sistemas diferentes incluyen rutas). En pocas palabras: para mí, prefiero tener más control y vivir con el error ocasional del software, en lugar de confiar en un compilador para que lo haga por mí.
Thomi
1

En mi proyecto (uso VS 2008 SP1) funciona la siguiente solución:

Archivo de cabecera:

//myclass.h
#pragma once
#define _WINSOCKAPI_
#include <windows.h>

Clase cpp:

//myclass.cpp
#include "Util.h"
#include "winsock2class.h"
#pragma comment(lib, "Ws2_32.lib")

donde #include "winsock2class.h" clase media que implementó winsock2.h:

//winsock2class.h
#include <winsock2.h>
#include <windows.h>
#pragma comment(lib, "Ws2_32.lib")
Yahor M
fuente
0

En realidad, me encontré con un problema en el que tenía que definir winsock2.h como la primera inclusión, parece tener otros problemas con las inclusiones de otros paquetes. Espero que esto sea útil para alguien que tenga el mismo problema, no solo windows.h, sino todo incluido.

Jeff
fuente