Ir ao conteúdo

Posts recomendados

Postado

como fazer 1 função write(), aceitando inteiros, floats, double, long, char e outros tipos?

void Write(int a,...)//podia usar Variant(sim tenho)
com va_list a; temos o ponteiro stack dos parametros 😛
mas não sabemos o 'count' ou sentinal(quero automatico)..
até agora consegui:
 

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <iostream>
#include <stdarg.h>

using namespace std;

// ============================================================
//  Variant (versão do utilizador)
// ============================================================
class Variant
{
    char*    data;
    void*    ptr;
    Variant* elementos;
    int      arrSize;

    void setString(const char* s)
    {
        if(!s) s = "";
        data = (char*)malloc(strlen(s) + 1);
        strcpy(data, s);
    }

public:
    enum Type { NONE=0, INT, STRING, DOUBLE, POINTER, LONG, ARRAY } type;

    void clear()
    {
        if(data) { free(data); data = NULL; }
        if(type == ARRAY && elementos) { delete[] elementos; elementos = NULL; arrSize = 0; }
        ptr  = NULL;
        type = NONE;
    }

    Variant()                : data(NULL), ptr(NULL), elementos(NULL), arrSize(0), type(NONE)   {}
    Variant(int i)           : data(NULL), ptr(NULL), elementos(NULL), arrSize(0), type(INT)    { char b[32]; sprintf(b,"%d",i);   setString(b); }
    Variant(long l)          : data(NULL), ptr(NULL), elementos(NULL), arrSize(0), type(LONG)   { char b[32]; sprintf(b,"%ld",l);  setString(b); }
    Variant(double d)        : data(NULL), ptr(NULL), elementos(NULL), arrSize(0), type(DOUBLE) { char b[64]; sprintf(b,"%g",d);   setString(b); }
    Variant(const char* s)   : data(NULL), ptr(NULL), elementos(NULL), arrSize(0), type(STRING) { setString(s); }
    Variant(void* p)         : data(NULL), ptr(p),    elementos(NULL), arrSize(0), type(POINTER){}
    Variant(char c)          : data(NULL), ptr(NULL), elementos(NULL), arrSize(0), type(STRING) { char b[2]; b[0]=c;       b[1]='\0'; setString(b); }
    Variant(unsigned char c) : data(NULL), ptr(NULL), elementos(NULL), arrSize(0), type(STRING) { char b[2]; b[0]=(char)c; b[1]='\0'; setString(b); }

    Variant(const Variant& o) : data(NULL), ptr(o.ptr), elementos(NULL), arrSize(o.arrSize), type(o.type)
    {
        if(o.data) { data=(char*)malloc(strlen(o.data)+1); strcpy(data,o.data); }
        if(type==ARRAY && o.elementos) { elementos=new Variant[arrSize]; for(int i=0;i<arrSize;i++) elementos[i]=o.elementos[i]; }
    }

    ~Variant() { if(data) free(data); if(type==ARRAY && elementos) delete[] elementos; }

    Variant& operator=(const char* s) { clear(); setString(s); type=STRING; return *this; }
    Variant& operator=(int i)         { clear(); char b[32]; sprintf(b,"%d",i);  setString(b); type=INT;    return *this; }
    Variant& operator=(long l)        { clear(); char b[32]; sprintf(b,"%ld",l); setString(b); type=LONG;   return *this; }
    Variant& operator=(double d)      { clear(); char b[64]; sprintf(b,"%g",d);  setString(b); type=DOUBLE; return *this; }

    Variant& operator=(const Variant& o)
    {
        if(this==&o) return *this;
        clear(); ptr=o.ptr; type=o.type; arrSize=o.arrSize;
        if(o.data) { data=(char*)malloc(strlen(o.data)+1); strcpy(data,o.data); }
        if(type==ARRAY && o.elementos) { elementos=new Variant[arrSize]; for(int i=0;i<arrSize;i++) elementos[i]=o.elementos[i]; }
        return *this;
    }

    bool operator==(const char* s) const { if(!s) s=""; if(!data) return strlen(s)==0; return strcmp(data,s)==0; }
    bool operator!=(const char* s) const { return !(*this==s); }

    operator const char*() const { return data ? data : ""; }
    const char* ToString() const { return data ? data : ""; }
    int         ToInt()    const { return data ? atoi(data) : 0; }
    double      ToDouble() const { return data ? atof(data) : 0.0; }
    Type        getType()  const { return type; }
	bool isNull()
	{
		return (type == NONE);   // NONE = não inicializado = NULL! ?
	}
    friend ostream& operator<<(ostream& os, const Variant& v) { os << (v.data ? v.data : ""); return os; }
};

struct VPeek
{
    char*          data;        // offset 0
    void*          ptr;         // offset 4
    void*          elementos;   // offset 8
    int            arrSize;     // offset 12
    Variant::Type  type;        // offset 16  <-- ULTIMO!
};


void Write(Variant a, ...)
{
    cout << a;

    va_list args;
    va_start(args, a);

    char* p = (char*)args;

    while(p > (char*)&a)
    {
        Variant v = *(Variant*)p;
        cout << v;

        p -= sizeof(Variant);
    }

    va_end(args);
}

int main()
{
    // assim todos sao Variant na stack -- ponteiros validos!
	Write("Ola mundo!!!", Variant(12));
    return 0;
}

resultado: "Ola mundo!!!12Press any key to continue"
qual é o atual problema:
1 - só mostra este 2 parametros(sim tentei com mais);
2 - tem de usar 'Variant(x)'.. amava aceita qualquer tipo!!!
isto tem tudo a ver com a stack 😛 mas estou com dificuldades em encontrar os dados\parametros 😞

  • Curtir 1
Postado

Isso que escreveu achei que ficou bem confuso pra dizer a verdade, incluindo a parte em português mesmo rs, mas vamos por partes:

 

  1. Ele só vai imprimir 2 mesmo pelo modo que escreveu, imprime o "a" e depois no laço while só vai passar uma vez mesmo, porque ele vai imprimir enquanto o ponteiro "p" estiver na frente do ponteiro de "a". Como no final do laço você volta o ponteiro com "p -= sizeof(Variant);", os 2 ponteiros vão ficar iguais aqui e sai do laço. Se quiser ir pra frente deveria ser "p += sizeof(Variant);", porém ele não ia parar nunca esse laço, não tem uma condição de final até o fim da memória...
  2. Pra aceitar qualquer tipo, você vai precisar ter um delimitador, não tem jeito, ou você passa primeiro a quantidade de parâmetros, ou no final da lista você passa um valor especial, como o ponteiro NULL ou um Variant específico pra demarcar o final, ou fazer como as funções *printf, que inclusive você usou ali a sprintf, onde você passa uma string indicando os tipos e quantos itens tem a frente.

Também o ideal seria você acessar os argumentos com o "va_arg", passando o tipo, ao invés de como fez ali, "*(Variant*)p", e ele já incrementa o ponteiro pra você.

 

Recomendo dar uma olhada aqui também, caso tenha dúvidas:

 

  • Curtir 1
  • Amei 1
Postado

mano pela função eu sei isso... mas quero evitar:
1 - usar  'count' ou sentinela!!!!
2 - o va_list é o ponteiro dos parametros.... mas não sabe a quantidade de parametros;
3 - tem haver com a stack... e sei que é possível passar por todos parametros e terminar em segurança... estou a tentar, mas ainda não consegui

  • Curtir 1
Postado
45 minutos atrás, Cambalinho disse:

1 - usar  'count' ou sentinela!!!!

 

Sim, a própria biblioteca do C usa isso, como exemplo que mostrei o "miniprintf" da resposta no Stackoverflow vai indo até o final da string formatada.

 

45 minutos atrás, Cambalinho disse:

2 - o va_list é o ponteiro dos parametros.... mas não sabe a quantidade de parametros;

 

Exatamente, um ponteiro em C não guarda outras informações.

 

45 minutos atrás, Cambalinho disse:

3 - tem haver com a stack... e sei que é possível passar por todos parametros e terminar em segurança... estou a tentar, mas ainda não consegui

 

Sim, mas só conheço com o contador/sentinela que você disse. Não tem outro jeito (que eu saiba), não há como adivinhar do nada quando o ponteiro vai apontar pra fora da área dos parâmetros que passou (que eu saiba rs). Ou no seu caso, poderia tentar verificar se a próxima posição do ponteiro tem um objeto "Variant" válido... aí já vai um pouco além do que conheço da linguagem rs.

 

Talvez isso aqui ajude, tem umas coisas em C++ que podem ser que te sirvam, mas eu mesmo já acho bem complicado e, sinceramente, pra que você vai querer fazer isso mesmo rs?

 

 

  • Curtir 2
Postado

eu uso(como estudo) anterior ao C98 😛

a stack da função...
objectivo: como posso obter o ponteiro do ultimo parametro? podemos usar __asm para isso..
em vez do count\sentinela, usamos o ponteiro do ultimo parametro 😉

imagem.png

  • Curtir 2
Postado

Você poderia usar um valor específico pra indicar o fim da sequencia de valores, como as strings da linguagem C que usam o caractere nulo '\0' pra indicar o fim da string.

 

Por exemplo, usando uma Variant() vazia pra indicar o fim, e um template pra fazer o cast dos argumentos para o tipo Variant e para sempre adicionar Variant() como último parâmetro passado para a função, ficaria assim:

 

void Write(Variant a, ...)
{
    cout << a;
    
    va_list args;
    va_start(args, &a);

    for(Variant p = va_arg(args, Variant); p != Variant(); p = va_arg(args, Variant))
    {
        cout << p;
    }

    va_end(args);
}

template<class... T>
void Write2(T... args)
{
   Write((Variant)args..., Variant());
}

int main()
{
	Write2("Ola mundo!!! ", 12, " ", 5);

    return 0;
}

 

  • Curtir 1
  • Obrigado 1
Postado

eu falei Cx0 😛
mas vais ver 😉
 

void Write(Variant a, ...)
{
    //Print the 1st parameter:
	cout << a;

	//Getting the variadic parameters:
    va_list args;
    va_start(args, a);
	
	//Getting  the last pointer parameter(using Assembler from stack):
    void* pFim = NULL;
    __asm
    {
        mov eax, [ebp+4]
        mov pFim, eax
    }

    unsigned char* pRet = (unsigned char*)pFim;
    int totalBytes = 0;

    if(pRet[2] == 0x83 && pRet[3] == 0xC4)
        totalBytes = pRet[4];

    char* pFimArgs = (char*)args + totalBytes - sizeof(Variant) + sizeof(void*);


    //throw parameters:
	char* p        = (char*)args;	
    while(p <= pFimArgs)//bug: losing the last parameter :(
    {
        int type = *(int*)(p + 16);
		
		//testing the type parameter:
        if(type >= Variant::NONE && type <= Variant::ARRAY)
        {
            Variant* v = (Variant*)p;
            cout << *v;
            p += sizeof(Variant);
        }
        else
        {
            int val = *(int*)p;
            if(val > 65536)
                cout << (const char*)val;
            else
            {
                char buf[32];
                sprintf(buf, "%d", val);
                cout << buf;
            }
            p += 4;
        }
    }
	
	
    va_end(args);
}

int main()
{
    Write("olá mundo!!!!", "ola ", "mundo",  "oi", "oiiii");
    return 0;
}

este codigo está lindo 😉
só perco o ultimo parametro 😞

  • Curtir 1
Postado

nesta versão:
 

void Write(Variant a, ...)
{
    cout << a;

    va_list args;
    va_start(args, a);

    void* pFim = NULL;
    __asm
    {
        mov eax, [ebp+4]
        mov pFim, eax
    }

    unsigned char* pRet = (unsigned char*)pFim;
    int totalBytes = 0;

    for(int i = 0; i < 16; i++)
    {
        if(pRet[i] == 0x83 && pRet[i+1] == 0xC4)
        {
            totalBytes = pRet[i+2];
            break;
        }
    }

    char* pFimArgs = (char*)args + (totalBytes - sizeof(Variant));
    char* p        = (char*)args;

    while(p < pFimArgs)
    {
        int   type = *(int*)(p + 16);
        char* data = *(char**)p;

        if(type > Variant::NONE && type <= Variant::ARRAY
           && (int)data > 65536)
        {
            Variant* v = (Variant*)p;
            cout << *v;
            p += sizeof(Variant);
        }
        else
        {
            int val = *(int*)p;

            if(val > 65536)
            {
                cout << (const char*)val;
                p += 4;
            }
            else
            {
                char buf[32];
                sprintf(buf, "%d", val);
                cout << buf;
                p += 4;
            }
        }
    }

    va_end(args);
}

int main()
{
    Write("olá mundo!!!!", "ola ", "mundo", Variant(345.66));
    return 0;
}

funciona, mas tenho de usar Variant(x)... sem usar macros, posso fazer com que seja automatico?

  • Curtir 1
Postado

Você poderia fazer isso bem mais simples usando um template variádico do C++, ao invés de uma função variádica:

 

void Write(){}

template<typename... Types>
void Write(Variant a, Types... args)
{
    cout << a;

    Write(args...);
}

int main()
{
	Write("Ola mundo!!! ", 12, " ", 34.879);
    return 0;
}

 

 

  • Curtir 1
Postado

consegui!!!! com ajuda do Gemini(IA):
 

void Write(Variant a, ...)
{
    cout << fixed << setprecision(3);

    va_list args;
    va_start(args, a);

    // --- HACK ASSEMBLY (Pega o tamanho da pilha) ---
    void* pRetAddr = NULL;
    __asm {
        mov eax, [ebp + 4]
        mov pRetAddr, eax
    }
    unsigned char* code = (unsigned char*)pRetAddr;
    int totalBytesNaStack = 0;
    for(int i = 0; i < 32; i++) {
        if(code[i] == 0x83 && code[i+1] == 0xC4) { totalBytesNaStack = code[i+2]; break; }
        if(code[i] == 0x81 && code[i+1] == 0xC4) { totalBytesNaStack = *(int*)&code[i+2]; break; }
    }

    char* pStart = (char*)&a;
    char* pEnd   = pStart + totalBytesNaStack;
    char* p      = pStart;

    while(p < pEnd) 
    {
        // 1. VARIANT (20 bytes)
        int supostoTipo = *(int*)(p + 16); 
        if(supostoTipo > 0 && supostoTipo <= 6) {
            Variant* v = (Variant*)p;
            cout << v->ToString();
            p += 20; continue;
        }

        unsigned int val = *(unsigned int*)p;

        // 2. STRINGS / PONTEIROS (4 bytes)
        if (val > 0x10000) {
            unsigned char* s = (unsigned char*)val;
            if (s[0] >= 32 && s[0] <= 126 && s[1] >= 32 && s[1] <= 126) {
                cout << (const char*)val;
            } else {
                cout << "ptr(0x" << hex << val << dec << ")";
            }
            p += 4; continue;
        }

        // 3. DOUBLE (8 bytes)
        if (p + 8 <= pEnd) {
            unsigned int highPart = *(unsigned int*)(p + 4);
            unsigned int exp = (highPart & 0x7FF00000) >> 20;
            if (exp >= 0x3FE && exp <= 0x40A) {
                cout << *(double*)p;
                p += 8; continue;
            }
        }

        // 4. O PULO DO GATO: Caractere vs Inteiro (4 bytes)
        // Se o valor está na faixa ASCII imprimível (32 a 126)
        // e os bytes superiores são zero (indicando que era um char promovido)
        if (val >= 32 && val <= 126) {
            cout << (char)val; // Imprime 'O' em vez de 79
        } else {
            cout << (int)val;  // Imprime números "reais" (como 10, 67, 20)
        }
        
        p += 4;
    }
    va_end(args);
    cout << endl;
}

 

C - Varidic Functions plus Assembler.pdf

  • Curtir 1

Crie uma conta ou entre para comentar

Você precisa ser um usuário para fazer um comentário

Criar uma conta

Crie uma nova conta em nossa comunidade. É fácil!

Crie uma nova conta

Entrar

Já tem uma conta? Faça o login.

Entrar agora

Sobre o Clube do Hardware

No ar desde 1996, o Clube do Hardware é uma das maiores, mais antigas e mais respeitadas comunidades sobre tecnologia do Brasil. Leia mais

Direitos autorais

Não permitimos a cópia ou reprodução do conteúdo do nosso site, fórum, newsletters e redes sociais, mesmo citando-se a fonte. Leia mais

×
×
  • Criar novo...