Arquivos de cabeçalho de redefinição em C ++ (winsock2.h)

143

Como impedir a inclusão de arquivos de cabeçalho duas vezes? O problema é que estou incluindo oem MyClass.h e então eu estou incluindo MyClass.h em muitos arquivos, assim que inclui várias vezes e erro redefinição ocorre. Como prevenir?

Estou usando #pragma uma vez em vez de incluir guardas, e acho que está bem.

MyClass.h:

// MyClass.h
#pragma once

#include <winsock2.h>

class MyClass
{

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

EDIT: Alguns dos erros que estou recebendo

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
fonte
4
Você já está usando o #pragma uma vez, portanto, ele deve ser incluído apenas uma vez.
Naveen
1
Seu compilador não suporta pragma uma vez?
Svetlozar Angelov 03/09/09
Estou usando o Visual Studio 2008, por que o <winsock2.h> é incluído duas vezes?
akif 03/09/09
1
Pode ser incluída duas vezes de alguns dos cabeçalhos incluídos de MyClass.h
Svetlozar Angelov
5
winsock2 e winsock têm estruturas comuns. Você tem que incluir apenas um deles, não a ambos
Svetlozar Angelov

Respostas:

234

Esse problema é causado ao incluir <windows.h>antes <winsock2.h>. Tente organizar sua lista de inclusão <windows.h>incluída depois <winsock2.h>ou defina _WINSOCKAPI_primeiro:

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

Veja também isso .

pingw33n
fonte
Não estou incluindo o <windows.h>, sei que o <winsock2.h> faz isso por mim.
akif 03/09/09
2
Para mim, seu código compila apenas <winsock2.h>com o MSVC2008. <windows.h>a inclusão gera erros de compilação idênticos aos fornecidos.
pingw33n 3/09/09
O <windows.h> está sendo incluído no stdafx.h?
Colin Desmond
1
Esta solução corrigiu o problema para mim no VS 2010 com o SDK 7.1. Obrigado pingw33n!
Adamfisk
Eu tinha #include <winsock2.h> #include <ws2tcpip.h> #include <windows.h>em ordem e estava recebendo winsock2, h arquivo não encontrado. Incluído #define _WINSOCKAPI_ acima de tudo 3 inclui ainda o mesmo erro
Ava
75

Como outros sugeriram, o problema é quando windows.hé incluído antes WinSock2.h. Porque windows.hinclui winsock.h. Você não pode usar ambos WinSock2.he winsock.h.

Soluções:

  • Incluir WinSock2.hantes windows.h. No caso de cabeçalhos pré-compilados, você deve resolvê-lo lá. No caso de um projeto simples, é fácil. No entanto, em grandes projetos (especialmente ao escrever código portátil, sem cabeçalhos pré-compilados), pode ser muito difícil, porque quando o cabeçalho WinSock2.hestá incluído, windows.hjá pode ser incluído em algum outro arquivo de cabeçalho / implementação.

  • Defina WIN32_LEAN_AND_MEANantes windows.hou projete todo. Mas excluirá muitas outras coisas que você pode precisar e deve incluir por conta própria.

  • Defina _WINSOCKAPI_antes windows.hou projete todo. Mas quando você inclui, WinSock2.hrecebe um aviso de redefinição de macro.

  • Use em windows.hvez de WinSock2.hquando winsock.hé suficiente para o seu projeto (na maioria dos casos, é). Provavelmente, isso resultará em um tempo de compilação mais longo, mas solucionará quaisquer erros / avisos.

Pavel Machyniak
fonte
14
WIN32_LEAN_AND_MEANfoi a solução para mim muitos tanques
Jonatan Cloutier
Sobre a _WINSOCK_solução: você não deve grt aviso de redefinição de macro se as duas definições forem idênticas. O erro comum é que as pessoas adicionam definição ao projeto sem definir nenhum valor e esperam uma definição vazia. No entanto, se você adicionar -D_WINSOCK_à linha cmd, ela será definida _WINSOCK_como 1. Para criar uma definição vazia, -D_WINSOCK_=deve ser passada.
Paweł Stankowski
Se você usar #define _WINSOCKAPI_, também poderá precisar #define _WINSOCK_DEPRECATED_NO_WARNINGS, dependendo das circunstâncias.
Lorien Brune
16

Ah - a feiura do Windows ... A ordem de inclusões é importante aqui. Você precisa incluir winsock2.h antes do windows.h. Como o windows.h provavelmente está incluído no cabeçalho pré-compilado (stdafx.h), você precisará incluir o winsock2.h a partir daí:

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

Usando "protetores de cabeçalho":

#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
fonte
2
Acho que estou errado (4 votos a essa altura), mas acho que usar o incluir guardas é o mesmo que pragma uma vez, você coloca os dois?
Svetlozar Angelov 03/09/09
1
Bem, eu tenho #pragma once que afaik é os mesmos guardas de cabeçalho
Akif
2
@Angelov: Sim, é o que estou dizendo que são as mesmas coisas. O problema não está nos meus arquivos de cabeçalho, mas acho que o <winsock2.h> em si não possui proteção de cabeçalho ou pode ser outra coisa.
akif 03/09/09
1
Por definição, #pragma depende do compilador (não padrão). Pode não funcionar em todos os compiladores. Eu sei que o visual studio aceita #pargma uma vez. Não tenho certeza se o gcc faz. Eu sei que incluem guardas SEMPRE trabalham. Uso o #pragma uma vez e incluo guardas para máxima proteção. Parece que o MSVC otimizou o tratamento do #pragma uma vez e o gcc otimizou o tratamento dos protetores de inclusão. A única diferença no meu cabeçalho padrão é que #praga já está fora dos protetores de inclusão.
KitsuneYMG 03/09/09
1
O comando '#pragma' é especificado no padrão ANSI para ter um efeito definido pela implementação arbitrariamente. No pré-processador GNU C, o '#pragma' tenta primeiro executar o jogo 'não autorizado'; se isso falhar, ele tenta executar o jogo 'hack'; se isso falhar, ele tenta executar o GNU Emacs exibindo a Torre de Hanói; se isso falhar, ele relata um erro fatal. De qualquer forma, o pré-processamento não continua. - Richard M. Stallman, o pré-processador GNU C, versão 1.34
DevSolar 03/09/09
6

Encontrei este problema ao tentar extrair um pacote de terceiros que aparentemente incluía o windows.h em algum lugar na sua bagunça de cabeçalhos. Definir _WINSOCKAPI_no nível do projeto era muito mais fácil (para não mencionar mais sustentável) do que percorrer a sopa e corrigir a inclusão problemática.

Yaur
fonte
1
Em Qt, no arquivo .pro, parece que esta: DEFINES += _WINSOCKAPI_
phyatt
@phyatt: você deve transformar isso em uma resposta, se não o fizer, eu irei!
Leif Gruenwoldt
@LeifGruenwoldt go for it! Ainda bem que pude ajudar.
Phyatt #
6

No VS 2015, o seguinte funcionará:

#define _WINSOCKAPI_

Enquanto o seguinte não:

#define WIN32_LEAN_AND_MEAN
MariuszW
fonte
6

Eu verifiquei o recursivo inclui, vi os arquivos de cabeçalho que incluem (de forma recursiva) alguns #include "windows.h"e #include "Winsock.h"e escrever um #include "Winsock2.h". nesses arquivos, eu adicionei #include "Winsock2.h"como a primeira inclusão.

Apenas uma questão de paciência, observe inclui um por um e estabeleça essa ordem, primeiro e #include "Winsock2.h"depois#include "windows.h"

Kiriloff
fonte
5

Encontrei este link windows.he winsock2.h, que tem uma alternativa que funcionou muito bem para mim:

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

Eu estava tendo problemas para encontrar onde o problema ocorreu, mas adicionando que #define eu era capaz de criar sem descobrir.

Benjamin Herreid
fonte
4

Eu não usaria apenas FILENAME_H, mas

#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

Eu sempre usei um guid postfix. Me deparei com uma base de código muito ruim, alguns anos atrás, que tinha arquivos de cabeçalho diferentes com o mesmo nome de arquivo e incluía guarda. Os arquivos em questão haviam definido uma classe com o mesmo nome. Se apenas namespaces fossem usados. Alguns projetos compilaram outros não. O uso de proteções exclusivas fazia parte da solução na diferenciação de cabeçalhos e seu conteúdo.

No Windows com Visual Studio, use guidgen.exe, no Linux uuidgen -t.

Sam
fonte
4

Corri para o mesmo problema e aqui está o que eu descobri até agora:

Deste fragmento de saída -

c: \ arquivos de programas \ microsoft sdks \ windows \ v6.0a \ include \ ws2def.h (91): aviso C4005: 'AF_IPX': redefinição de macro
c: \ arquivos de programas \ microsoft sdks \ windows \ v6.0a \ include \ winsock.h (460): consulte a definição anterior de 'AF_IPX'

-Parece que ws2def.he winsock.h foram incluídos na sua solução.

Se você olhar para o arquivo ws2def.h, ele começará com o seguinte comentário -

/*++

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 atenção na última linha - "Este arquivo não pode ser incluído por um módulo que também inclui o WINSOCK.H"

Ainda tentando corrigir o problema sem fazer alterações no código.

Deixe-me saber se isso faz sentido.

Shailesh Tainwala
fonte
2

Você deve usar o protetor de cabeçalho.

coloque essas linhas no topo do arquivo de cabeçalho

#ifndef PATH_FILENAME_H
#define PATH_FILENAME_H

e no fundo

#endif
ntcong
fonte
1
#pragma uma vez e incluir guardas são as mesmas coisas, não são?
akif 3/09/09
Eles não são exatamente iguais - os protetores de cabeçalho impedirão a re-inclusão do arquivo no nível do pré-processador, além de obviamente serem um pouco mais portáteis do que o #pragma uma vez.
Timo Geusch 03/09/09
1
Eu quis dizer que eles são construídos para os mesmos fins :)
akif
2
#pragma uma vez que é não-padrão, afaik
ntcong
2

#pragma onceé baseado no caminho completo do nome do arquivo. Portanto, o que você provavelmente tem é que existem duas cópias idênticas do MyClass.h ou Winsock2.h em diretórios diferentes.

soru
fonte
um link simbólico ou junção NTFS também fará com que o sistema quebre.
Thomi 03/09/09
1

#pragma onceé flakey, mesmo em compiladores MS, e não é suportado por muitos outros compiladores. Como muitas outras pessoas mencionaram, usar os protetores de inclusão é o caminho a seguir. Não use #pragma oncenada - isso facilitará sua vida.

Thomi
fonte
3
Infelizmente, vi mais que zero falhas de proteção, onde um erro de digitação significa que a proteção realmente não funciona ou onde arquivos com o mesmo nome em diretórios diferentes usam o mesmo token ou quando o token usado começa com um duplo sublinhado ou sublinhado e depois maiúscula (e, portanto, não é portátil, como #pragma uma vez). Portanto, para código inerentemente não portátil, como qualquer coisa usando winsock.h, fiquei profundamente incomodado com o #pragma uma vez até o ponto em que você disse que era flakey. Quando falha, além de não ter suporte algum?
Steve Jessop
3
Ao usar #pragma once, o compilador leva o nome do nó do arquivo de cabeçalho como o ID exclusivo. Isso pode falhar se você tiver links simbólicos ou junções NTFS em sua árvore de origem (mais comuns do que você imagina), ou mesmo se você tiver um arquivo com o mesmo nome em outro diretório de inclusão do sistema (isso já aconteceu comigo antes quando versão 1 e versão 2 da mesma biblioteca instalada em dois caminhos de inclusão diferentes do sistema). Conclusão: para mim, prefiro ter mais controle e conviver com os erros ocasionais de wetware, em vez de confiar em um compilador para fazer isso por mim.
Thomi
1

#include guardas são a maneira padrão de fazer isso. #pragma uma vez não é, o que significa que nem todos os compiladores o suportam.

Dima
fonte
1

No meu projeto (eu uso o VS 2008 SP1) funciona a próxima solução:

Arquivo de cabeçalho:

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

Classe Cpp:

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

onde #include "winsock2class.h" significa a classe que implementou winsock2.h:

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

Na verdade, eu tive um problema em que tive que definir o winsock2.h como o primeiro include, parece que ele tem outros problemas com inclusões de outros pacotes. Espero que isso seja útil para alguém que tenha o mesmo problema, não apenas o windows.h, mas tudo incluído.

Jeff
fonte