Προς το περιεχόμενο

Προτεινόμενες αναρτήσεις

Δημοσ. (επεξεργασμένο)

Καλησπέρα,

 

θα ήθελα τη γνώμη σας για το θέμα του τίτλου. Η υλοποίηση τέτοιων στοιβών (και γενικώς linked lists) γνωρίζουμε πως επιτυγχάνεται με δείκτη void στο πεδίο data του κάθε κόμβου της στοίβας.

 

>
typedef struct Stack {
void *pdata;
struct Stack *down;
} Stack;

 

Φτιάχνοντας ένα interface για στοίβες ετερογενών κόμβων και δεδομένου ότι οι void pointers δεν μπορούν να γίνουν dereferenced, πως θα διαχειριζόσασταν την ανάγκη εκτύπωσης των δεδομένων στον κάθε κόμβο;

 

Μου ζητήθηκε η ανάπτυξη ενός τέτοιου interface και προς το παρόν έχω πάρει τον δρόμο του union, με χρήση type-specifiers συνεπών με τους αντίστοιχους της γλώσσας, τόσο για την εισαγωγή (push) όσο και για την εκτύπωση (top), για εγγενή υποστήριξη ορισμένων βασικών τύπων (char, char *, int, double προς το παρόν).

 

Δηλαδή...

 

>
typedef union DataValue {
long double valundefined;
char	valchar;
int	valint;
double	valdouble;
char	*valstring;
}DataValue;

typedef enum DataTypeID {
DTYPE_NOID = 0,
DTYPE_CHAR,
DTYPE_INT,
DTYPE_DOUBLE,
DTYPE_STRING,
/* must always be last (not a type, just their total count ) */
DTYPE_MAX
}DataTypeID;

typedef struct Stack {
DataTypeID dtypeId;
void	*pdata;
unsigned int count;
struct 	Stack *down;
}Stack;

int stack_push( Stack **stack, const char *fmt, ... );

 

Οπότε για παράδειγμα, ο προγραμματιστής να μπορεί να χρησιμοποιεί type-specifiers α-λα printf() για την εισαγωγή κόμβων στην στοίβα...

 

>
Stack *stack = NULL;

stack_push( &stack, "%c", 'a');
stack_push( &stack, "%f", 123.5);   /* γίνεται promoted σε double, λόγω της variadic φύσης της stack_push */
stack_push( &stack, "%s", "This is a string");
...

 

Βάση του type-specifier ο κώδικας της stack_push() σετάρει το πεδίο typeId στον κόμβο, βάζει την τιμή που δόθηκε στο κατάλληλο πεδίο του union (που γίνεται malloc() μέσα στη συνάρτηση μέσω τοπικού δείκτη pdataval ) και θέτει τον pdata του κόμβου να δείχνει στα περιεχόμενα του pdataval.

 

Για όποιον ενδιαφέρεται, ο μέχρι στιγμής κώδικας της stack_push() είναι ο εξής...

 

 

 

>
int stack_push( Stack **stack, const char *fmt, ... )
{
extern char *g_dtypes[DTYPE_MAX][2];	/* typelabels and their % specifiers  */

va_list ap;				/* for parsing the arguments list 	*/
DataValue *pdataval = NULL;		/* pointer to union of value types	*/
Stack *newnode = NULL;			/* new node to be added to the stack  */

/* sanity check */
if ( !stack )
	return 0;

newnode = malloc( sizeof(Stack) );
if ( !newnode )
	return 0;

/* determine the type of the 1st variadic argument and store its value in pdataval */

va_start(ap, fmt);
if ( !strncmp(fmt, g_dtypes[DTYPE_INT][1], 2) )		/* (%d) integer specifier */
{
	pdataval = malloc( sizeof(int) );
	if ( !pdataval )
		goto clean_and_ret_failure;
	pdataval->valint = va_arg(ap, int);
	newnode->dtypeId = DTYPE_INT;
}
else if ( !strncmp(fmt, g_dtypes[DTYPE_CHAR][1], 2) )	/* (%c) char specifier */
{
	pdataval = malloc( sizeof(char) );
	if ( !pdataval )
		goto clean_and_ret_failure;
	pdataval->valchar = (char)va_arg(ap, int);
	newnode->dtypeId = DTYPE_CHAR;
}
							/* (%g) double/float specifier*/
else if ( !strncmp( fmt, g_dtypes[DTYPE_DOUBLE][1], 2)
|| !strncmp(fmt, "%e", 2) || !strncmp(fmt, "%f", 2) )
{
	pdataval = malloc( sizeof(double) );
	if ( !pdataval )
		goto clean_and_ret_failure;
	pdataval->valdouble = va_arg(ap, double);
	newnode->dtypeId = DTYPE_DOUBLE;
}
else if ( !strncmp(fmt, g_dtypes[DTYPE_STRING][1], 2) )	/* (%s) string specifier */
{
	char *cp = NULL;
	size_t instrsize = 0;

	pdataval = malloc( sizeof(char *) );
	if ( !pdataval )
		goto clean_and_ret_failure;
	cp = va_arg(ap, char *);
	instrsize = strlen( cp ) + 1;
	pdataval->valstring = malloc( instrsize * sizeof(char) );
	if ( !pdataval->valstring ) {
		free( pdataval );
		goto clean_and_ret_failure;
	}
	memcpy(pdataval->valstring, cp, instrsize * sizeof(char) );
	newnode->dtypeId = DTYPE_STRING;
}
else	goto clean_and_ret_failure;

va_end( ap );

newnode->pdata = pdataval;
newnode->count = !(*stack) ? 1 : (*stack)->count + 1;
newnode->down = *stack;
*stack = newnode;

return 1;

clean_and_ret_failure:

free( newnode );
va_end( ap );
return 0;
}

 

 

Με αυτόν τον τρόπο προσπαθώ να αυτοματοποιήσω την εκτύπωση βασικών τύπων, αφού πλέον μέσω του dtypeId πεδίου στον κάθε κόμβο γνωρίζω με ποιον type-specifier πρέπει να το τυπώσω στην οθόνη με την printf().

 

Π.χ...

 

 

 

>
char *g_dtypes[DTYPE_MAX][2] = {
{"undefined",	"%0"},
{"char",	"%c"},
{"int",		"%d"},
{"double",	"%g"},
{"string",	"%s"}
};

/* -----------------------------------------------------------------
*
* -----------------------------------------------------------------
*/
static int stack_data_dump( const DataTypeID dtypeId, const DataValue *pdata )
{
extern char *g_dtypes[DTYPE_MAX][2];
char tempfmt[100+1] = {'\0'}, typelabel[100+1] = {'\0'};

/* construct a temp string holding the typelabel enclosed in parentheses */
sprintf( typelabel, "(%s)", g_dtypes[dtypeId][0] );

/* construct the sprintf formating template: "(typelabel) %?\n" */
sprintf(tempfmt, "%-8s %s\n", typelabel, g_dtypes[dtypeId][1]);

/* finally, print the contents of pdata*/
printf(tempfmt, pdata->valundefined );

return -1;
}
/* -----------------------------------------------------------------
*
* -----------------------------------------------------------------
*/
void stack_dump( Stack *stack )
{
if ( !stack ) {
	printf("\tthe stack is empty or non-existant");
	return;
}

while ( stack )
{
	printf("%5u : ", stack->count);
	stack_data_dump( stack->dtypeId, stack->pdata );
	stack = stack->down;
}

return;
}

 

 

που δίνει output της παρακάτω μορφής...

 

>
12 : (string) This is a string
11 : (double) 12.45
10 : (int)	9
9 : (int)	8
8 : (int)	7
7 : (int)	6
6 : (int)	5
5 : (int)	4
4 : (int)	3
3 : (int)	2
2 : (int)	1
1 : (int)	0

 

Παραβλέποντας προς το παρόν την υποστήριξη και για άλλους στάνταρ τύπους (που είναι απλά θέμα μερικών προσθηκών στα παραπάνω), πρέπει να προστεθεί υποστήριξη μη στάνταρ τύπων, που εν πολλοίς σημαίνει custom structures μέσω δεικτών.

 

Αυτό είναι πιο tricky, αφού ναι μεν θα προστεθεί ένας ακόμα type-specifier για custom data types (π.χ. %0 που το έχω ήδη, αλλά unused προς το παρόν) και θα εισαγάγω κανονικά στην στοίβα με ξεχωριστό dtypeId στον κόμβο, αλλά ο μόνος τρόπος που μπορώ να σκεφτώ για την εκτύπωση των στοιχείων αυτού του κόμβου είναι μέσω ενός δείκτη σε συνάρτηση εκτύπωσης που θα την παρέχει ο προγραμματιστής (μιας κι εγώ δεν μπορώ να ξέρω τι, πόσα και ποια στοιχεία θα περιέχει).

 

Θέλω λοιπόν τη γνώμη σας για πιθανές καλύτερες υλοποιήσεις, γιατί νομίζω αρχίζει να μου φαίνεται λίγο over-complicated έτσι.

 

Έχει κανείς σας εμπειρία με τέτοιους είδους προγράμματα σε C. Σας φαίνεται OK όπως είναι τώρα, πριν συνεχίσω.

 

Thanks εκ των προτέρων σε όποιον ασχοληθεί (και μόνο το διάβασμα του post θέλει την ώρα του :lol:).

 

EDIT:

Διόρθωση του κώδικα της stack_push()

Επεξ/σία από migf1
  • Απαντ. 37
  • Δημ.
  • Τελ. απάντηση

Συχνή συμμετοχή στο θέμα

Συχνή συμμετοχή στο θέμα

Δημοσ.

[..]αλλά ο μόνος τρόπος που μπορώ να σκεφτώ για την εκτύπωση των στοιχείων αυτού του κόμβου είναι μέσω ενός δείκτη σε συνάρτηση εκτύπωσης που θα την παρέχει ο προγραμματιστής (μιας κι εγώ δεν μπορώ να ξέρω τι, πόσα και ποια στοιχεία θα περιέχει).

Προσωπικά και εγώ σε μια τέτοια λύση θα κατέληγα, από την στιγμή που είναι custom δεδομένα ας τα χειριστεί μόνος του όπως κρίνει καλύτερα μέσο του ανάλογου print function pointer (με ανάλογα ευέλικτο τρόπο δουλεύει άλλωστε και η qsort -fcmp).

Δημοσ.

Προσωπικά και εγώ σε μια τέτοια λύση θα κατέληγα, από την στιγμή που είναι custom δεδομένα ας τα χειριστεί μόνος του όπως κρίνει καλύτερα μέσο του ανάλογου function pointer.

 

:) :) :) (που είναι το kiss smiley? :lol:)

 

Ως γενικότερο implementation μέχρι στιγμής το βρίσκεις καλό ως έχει (μιας και θέλουν auto-support στα basic data types)? Υπάρχει καμιά άλλη τεχνική που θα το απλοποιούσε, έχεις κάτι υπόψη σου; Εννοώ το structure, τις δομές, κλπ.

 

EDIT:

Επίσης, η προσέγγιση με τα type-specifiers είναι overkill πιστεύεις; Θα ήταν καλύτερα αντί για "%?" να τους βάζω να δίνουν το DTYPE_ID στην stack_push() ? Το έβαλα για να τους είναι πιο εύκολο να θυμούνται τα types, αλλά δεν είμαι σίγουρος αν αξίζει τον κόπο.

Δημοσ.

Καταρχήν πολύ καλή ιδέα γενικά :)

 

Από εκεί και πέρα, η αυτόματη υποστήριξη βασικών τύπων της C μου αρέσει πολύ, βοηθάει τον προγραμματιστή και προσδίδει ως έναν βαθμό type-safety σε μια typeless γλώσσα όπως η C. Οπότε λύνει χέρια και θυμίζει αρκετά υλοποιήσεις πιο μοντέρνων γλωσσών (και περισσότερο οικείων σε νεώτερους προγραμματιστές). Η χρήση "%?" καθιστά πιο φιλικό και ευανάγνωστο τον κώδικα, άλλωστε όλοι οι προγραμματιστές της οικογένειας των C-like γλωσσών έχουμε μάθει πια %c => char, %d => int κλπ, οπότε δεν με προβληματίζει. Από την στιγμή που υποστηρίζεις generic τύπους είσαι καλυμμένος.

Δημοσ.

Καταρχήν πολύ καλή ιδέα γενικά :)

 

Από εκεί και πέρα, η αυτόματη υποστήριξη βασικών τύπων της C μου αρέσει πολύ, βοηθάει τον προγραμματιστή και προσδίδει ως έναν βαθμό type-safety σε μια typeless γλώσσα όπως η C. Οπότε λύνει χέρια και θυμίζει αρκετά υλοποιήσεις πιο μοντέρνων γλωσσών (και περισσότερο οικείων σε νεώτερους προγραμματιστές). Η χρήση "%?" καθιστά πιο φιλικό και ευανάγνωστο τον κώδικα, άλλωστε όλοι οι προγραμματιστές της οικογένειας των C-like γλωσσών έχουμε μάθει πια %c => char, %d => int κλπ, οπότε δεν με προβληματίζει.

 

Κάνω επίσημα αίτηση να προστεθεί kissing smiley στο φόρουμ του Insomnia :lol:

 

Thanks ρε συ, μου έδιωξες ένα βάρος, γιατί νόμιζα πως είχα αρχίσει να ξεφεύγω αχρείαστα (και ξέρεις, δεν θέλω κιόλας να τους ρωτάω συνέχεια αυτό το θέλετε έτσι, το άλλο αλλιώς, κλπ μην μας περάσουν και για ανασφαλείς :lol: ).

 

Θέλω να τους κάνω πρώτα ένα 1st draft του interface όπως νομίζω εγώ πως θα εξυπηρετεί καλύτερα, με τα ζητούμενα που μου έδωσαν, και μετά θα συζητήσουμε για ενδεχόμενες αλλαγές. Δεν τα έχω όλα ξεκαθαρισμένα ακόμα μέχρι τελικής λεπτομέρειας, αλλά ήθελα να βεβαιωθώ πως ο σκελετός πάνω στον οποίον επέλεξα να πατήσω δεν είναι παρωχημένος ;)

 

ΥΓ1. Αν ξέρει κάποιος κάποια άλλη καλύτερη δομική υλοποίηση εντός τέτοιου project, please share it.

 

ΥΓ2. Btw, ο κώδικας της stack_push() έχει bug, τερματίζει τη συνάρτηση χωρίς va_end() θα το διορθώσω σε λιγάκι.

Δημοσ.

Εμένα η όλη κατάσταση με τους printf-like specifiers μου φαίνεται κακή ιδέα.

 

Τι κερδίζεις;

 

Αντί να γράψει κάποιος

 

>stack_push( &stack, DTYPE_CHAR, 'a');

 

του επιτρέπεις να γράψει

 

>stack_push( &stack, "%c", 'a');

 

Δεν βλέπω γιατί το δεύτερο είναι προτιμότερο από το πρώτο. Αν βλέπει κάποιος άλλος παρακαλώ να το αναλύσει λίγο.

 

Τι χάνεις

 

Βασικά μετατρέπεις όλα τα compile-time τυπογραφικά errors σε runtime errors => κακή φάση.

 

Π.χ. αν έγραφα

 

>stack_push( &stack, DTYPE_TREXAGYREYE, 'a');

 

θα το έπιανε ο compiler, ενώ αν γράψω π.χ.

 

>stack_push( &stack, "%q", 'a');

 

δεν θα το πιάσει.

 

Type safety

 

Εδώ θα πρέπει να διαφωνήσω κάθετα με τον φίλο Directx και ελπίζω να μην το πάρει προσωπικά.

 

Από εκεί και πέρα, η αυτόματη υποστήριξη βασικών τύπων της C μου αρέσει πολύ, βοηθάει τον προγραμματιστή και προσδίδει ως έναν βαθμό type-safety σε μια typeless γλώσσα όπως η C.

 

Καταρχήν η C είναι κάθε άλλο παρά typeless γλώσσα, θα τρίζουν τα κόκκαλα του Ritchie με κάτι τέτοια.

 

Επιπλέον, αυτό που συμβαίνει εδώ είναι ακριβώς το αντίθετο του type safety! Όχι μόνο δεν καταλαβαίνει ο compiler πως έγραψε κανείς @@ρια, αλλά ούτε καν δεν καταλαβαίνει ότι έκανες και τυπογραφικά!

 

Για σύγκριση λοιπόν:

 

SUPER UNSAFE:

 

>stack_push( &stack, "%c", 'a');

 

Εδώ κάνεις το σταυρό σου και πατάς run, o compiler δεν πρόκειται να πιάσει τίποτα. Μόνο και μόνο που η function είναι variadic τελειώνει η κουβέντα περι safety.

 

ΛΙΓΟ ΠΙΟ ΑΣΦΑΛΕΣ:

 

>stack_push( &stack, DTYPE_CHAR, 'a');

 

Αυτό που είπα παραπάνω. Τουλάχιστον εδώ πιάνεται τυχόν τυπογραφικό στο DTYPE_.

 

ΕΝΑΛΛΑΚΤΙΚΑ (με το σταυρό στο χέρι):

 

>stack_push_char( &stack, 'a');

 

(εννοείται και επιπλέον αντίστοιχες για τα υπόλοιπα primitive types)

 

Εκ πρώτης όψεως δεν υπάρχει κάτι καλύτερο εδώ σε σχέση με το προηγούμενο (ίσα ίσα που είναι "χειρότερο" με την έννοια ότι πλέον είσαι αναγκασμένος να κάνεις hardcode το data type, και μάλιστα χωρίς να κερδίζεις κάτι γιατί ισχύουν τα implicit conversions).

 

Ενδεχομένως όμως τελικά να κερδίζεις κάτι:

  1. μπορεί ο compiler να δίνει warnings σε τέτοια implicit conversions
  2. μπορεί να χρησιμοποιείς κάποιο lint-like εργαλείο που να δίνει τέτοια warnings

 

Οπότε ίσως έτσι να έχεις κάποια ελπίδα να πιάσεις type errors κατά το compilation.

 

Από την άλλη, δυσκολεύομαι να φανταστώ κάποια περίπτωση όπου από τη μία χρειάζεσαι variadic stack και από την άλλη κάνεις push statically known types μέσα (π.χ. αν λόγω αλγορίθμου πάντα μετά από ένα "i" κάνεις push 2 ints, τότε γιατί δεν τα πακετάρεις όλα αυτά σε ένα struct το οποίο να κάνεις push "με τη μία"?).

Δημοσ.

 

Thanks για το link!

 

Του έριξα μόλις μια ματιά, αλλά τι σε κάνει να πιστεύεις πως μπορώ να βγάλω άκρη; :lol: Ο χαμός του χαμού! Αν το 'πιασα σωστά, χρησιμοποιούν μια συνάρτηση:

static void type(FICL_VM *pVM)

 

με το FICL_VM να συνδέεται με ένα virtual machine, που για να κάνει push στο stack του χρησιμοποιεί εξειδικευμένη συνάρτηση για integers:

void stackPushINT(FICL_STACK *pStack, FICL_INT i)

 

Δεν βρήκα επίσης κάτι που να τυπώνει τιμές σε διαφορετικά datatype formats... που να βγάλω άκρη εκεί πέρα ρε συ; Έχεις εντοπίσει κάτι χρήσιμο για την περίπτωσή μου εκεί πέρα; Αν ναι, έχεις κάποιο πιο εξειδικευμένο link;

 

 

 

Εμένα η όλη κατάσταση με τους printf-like specifiers μου φαίνεται κακή ιδέα.

 

Τι κερδίζεις;

 

Αντί να γράψει κάποιος

 

>stack_push( &stack, DTYPE_CHAR, 'a');

 

του επιτρέπεις να γράψει

 

>stack_push( &stack, "%c", 'a');

 

Δεν βλέπω γιατί το δεύτερο είναι προτιμότερο από το πρώτο. Αν βλέπει κάποιος άλλος παρακαλώ να το αναλύσει λίγο.

 

Τι χάνεις

 

Βασικά μετατρέπεις όλα τα compile-time τυπογραφικά errors σε runtime errors => κακή φάση.

 

Π.χ. αν έγραφα

 

>stack_push( &stack, DTYPE_TREXAGYREYE, 'a');

 

θα το έπιανε ο compiler, ενώ αν γράψω π.χ.

 

>stack_push( &stack, "%q", 'a');

 

δεν θα το πιάσει.

 

Type safety

 

Εδώ θα πρέπει να διαφωνήσω κάθετα με τον φίλο Directx και ελπίζω να μην το πάρει προσωπικά.

 

 

 

Καταρχήν η C είναι κάθε άλλο παρά typeless γλώσσα, θα τρίζουν τα κόκκαλα του Ritchie με κάτι τέτοια.

 

Επιπλέον, αυτό που συμβαίνει εδώ είναι ακριβώς το αντίθετο του type safety! Όχι μόνο δεν καταλαβαίνει ο compiler πως έγραψε κανείς @@ρια, αλλά ούτε καν δεν καταλαβαίνει ότι έκανες και τυπογραφικά!

 

Για σύγκριση λοιπόν:

 

SUPER UNSAFE:

 

>stack_push( &stack, "%c", 'a');

 

Εδώ κάνεις το σταυρό σου και πατάς run, o compiler δεν πρόκειται να πιάσει τίποτα. Μόνο και μόνο που η function είναι variadic τελειώνει η κουβέντα περι safety.

 

ΛΙΓΟ ΠΙΟ ΑΣΦΑΛΕΣ:

 

>stack_push( &stack, DTYPE_CHAR, 'a');

 

Αυτό που είπα παραπάνω. Τουλάχιστον εδώ πιάνεται τυχόν τυπογραφικό στο DTYPE_.

 

ΕΝΑΛΛΑΚΤΙΚΑ (με το σταυρό στο χέρι):

 

>stack_push_char( &stack, 'a');

 

(εννοείται και επιπλέον αντίστοιχες για τα υπόλοιπα primitive types)

 

Εκ πρώτης όψεως δεν υπάρχει κάτι καλύτερο εδώ σε σχέση με το προηγούμενο (ίσα ίσα που είναι "χειρότερο" με την έννοια ότι πλέον είσαι αναγκασμένος να κάνεις hardcode το data type, και μάλιστα χωρίς να κερδίζεις κάτι γιατί ισχύουν τα implicit conversions).

 

Ενδεχομένως όμως τελικά να κερδίζεις κάτι:

  1. μπορεί ο compiler να δίνει warnings σε τέτοια implicit conversions
  2. μπορεί να χρησιμοποιείς κάποιο lint-like εργαλείο που να δίνει τέτοια warnings

 

Οπότε ίσως έτσι να έχεις κάποια ελπίδα να πιάσεις type errors κατά το compilation.

 

Από την άλλη, δυσκολεύομαι να φανταστώ κάποια περίπτωση όπου από τη μία χρειάζεσαι variadic stack και από την άλλη κάνεις push statically known types μέσα (π.χ. αν λόγω αλγορίθμου πάντα μετά από ένα "i" κάνεις push 2 ints, τότε γιατί δεν τα πακετάρεις όλα αυτά σε ένα struct το οποίο να κάνεις push "με τη μία"?).

 

 

 

Είναι valid points αυτά που θίγεις φίλε defacer, τα οποία με προβληματίζουν και μένα. Για αυτό και ζήτησα τη γνώμη σας.

 

Type safety όπως λες κι εσύ δεν υπάρχει, είτε με είτε χωρίς type-specifiers, εφόσον την έχουμε variadic την stack_push()... εκτός αν αρχίσω να κάνω τίποτα macro-παπαδιές, όπως π.χ. αυτό εδώ: http://modelingwithd...ch/00000022.htm (που κι αυτό δυστυχώς είναι C99, ενώ το θέλουν C89)

 

Από αυτή την άποψη η καλύτερη λύση είναι αυτή που επίσης είπες, ξεχωριστή συνάρτηση για κάθε ξεχωριστό primitive datatype, όπου κι εκεί όμως θα πρέπει να γίνει τουλάχιστον ένας εσωτερικός έλεγχος για matching των sizeof αυτού που περιμένουμε κι αυτού που παίρνουμε (το οποίο από μόνο του και πάλι δεν αρκεί, θα παίρνουμε warnings όμως από τον compiler.

 

Η ιδέα για τους type-specifiers μου ήρθε για να μη χρειάζεται να αποστηθίσει ο προγραμματιστής τα enumaration values, αλλά κυρίως για να μπορούν να φύγουν τελείως από το σκηνικό (data hiding) χωρίς να χρειάζεται καν να τα γνωρίζει ο προγραμματιστής όταν χρησιμοποιεί το interface.

 

Και προφανώς πρόκειται as usual, για trade-off. Για αυτό ζητάω και γνώμες.

 

Βέβαια, η χρήση των specifiers δεν είναι τόσο τραγική όσο την παρουσίασες, γιατί και τυπογραφικό λάθος να κάνεις το μόνο που θα γίνει είναι απλά να μην γίνει pushed τίποτα στο stack. Αυτό συμβαίνει ήδη, μένει όμως να αποφασίσω αν πρέπει να γίνεται silently όπως τώρα ή loudly (ή semi-silently, με χρήση του errno). Ακόμα και τώρα που ουσιαστικά είναι κάτι λιγότερο από 1st draft, επιστρέφει 0 η stack_push() για να δείξει στον προγραμματιστή πως δεν έγινε το push.

 

Οπότε δίνετε γνώμες, πάντα είναι χρήσιμη η πολυφωνία.

 

Επίσης μόλις είδα μια λαλακία που κάνω. Στον τοπικό δείκτη pdataval κάνω malloc ακριβώς το sizeof του expected datatype, του αναθέτω την τιμή που μου περνάει και βάζω κατόπιν τον pdata να δείχνει στην allocated μνήμη. Κανονικά πρέπει να κάνω allocate sizeof(union) και κατόπιν να κάνω την ανάθεση με cast!!!

 

Ή μήπως όχι; Πω πω τα 'παιξα, ώρα για διάλειμμα μου φαίνεται (ή για total stop για σήμερα ).

Δημοσ. (επεξεργασμένο)

Πρωτα πρωτα, ασχολησου με c++ να χαρεις template!!!!!111

 

Λοιπων, δεν βλεπω το λογο να εχεις vararg. Γιατι να εχεις; Παντα το φορματ θα περιεχει εναν τυπο αρα παντα τα args θα ειναι 1, αρα αντι να εχεις args μπορεις απλα να βαλεις void* και καθαρισες.

Καλεις δυο φορες malloc μια για φτιαξεις το node και αλλη για να φτιαξεις το data που δεν ξεπερναει τα 12 byte.

Δηλαδη εχεις αυτο

stack1.jpg

Αυτο μπορεις να το κανεις στο ιδιο ptr και να μην εχεις του malloc το καγκελο

Δηλαδη

stack2.png

Σε κωδικα κατι τετοιο

typedef struct

>[/size]
[size="2"]{[/size]
[size="2"]	int foo;[/size]
[size="2"]	int fee;[/size]
[size="2"]	int bar;[/size]
[size="2"]	char data[1];[/size]
[size="2"]}TheStruct;[/size]
[size="2"]
[/size]
[size="2"]void foo_add(TheStruct** bar,void* pData,size_t cbData)[/size]
[size="2"]{[/size]
[size="2"]	*bar = (TheStruct*) malloc((size_t)&((TheStruct*)0)->data + cbData);[/size]
[size="2"]	memcpy( (*bar)->data,pData,cbData);[/size]
[size="2"]}[/size]
[size="2"]void foo_get(TheStruct** bar,void* pData,size_t cbData)[/size]
[size="2"]{[/size]
[size="2"]	memcpy(pData,(*bar)->data,cbData);[/size]
[size="2"]}[/size]
[size="2"]int main(int,char**)[/size]
[size="2"]{[/size]
[size="2"]	TheStruct* ts;[/size]
[size="2"]	long double ld = 1.001243e10,ld1;[/size]
[size="2"]	foo_add(&ts,&ld,sizeof(ld));[/size]
[size="2"]	foo_get(&ts,&ld1,sizeof(ld1));[/size]
[size="2"]	std::cout<<[/size]
[size="2"]		ld1[/size]
[size="2"]		;[/size]
[size="2"]
[/size]
[size="2"]	return 0;[/size]
[size="2"]
[/size][size="2"]
}[/size][size="2"]

 

Τελος, το εχεις κανει πολυ πολυπλοκο χωρις λογο. Πχ εχεις union και δεν εκμεταλλευεσαι τιποτα απο τα καλα που εχει.

 

Επεξ/σία από παπι
Δημοσ.

Thanks για την απάντηση! Βγάλε αν θες εκείνα τα [ size ] tags από τον κώδικα για να μπορεί να διαβαστεί, αλλά θα το δω αργότερα γιατί έχω γίνει κουνουπίδι και δεν λειτουργώ τώρα. Αν όχι αργότερα (που θα πάω σπίτι) τότε σίγουρα αύριο,

 

ΥΓ. Το union το εκμεταλλεύομαι στην push αντί για type-casting (οπότε άκυρο και το τελευταίο σχόλιό μου στο προηγούμενο post) ... το variadic μου φάνηκε καλή λύση για αποφυγή αποφυγή ξεχωριστών συναρτήσεων για κάθε datatype... αλλά για σίγουρα από αύριο, που θα λειτουργώ :)

 

EDIT:

 

Με το void * dereference είχα φάει ένα κόλλημα, για αυτό και η ύπαρξη του union και το malloc() σε τοπική μεταβλητή πριν περαστεί στο κανονικό pdata... btw, 1 είναι μόνο το malloc(), 2ο γίνεται μονάχα στην περίπτωση του string. Θα δω τον κώδικά σου όμως, γιατί τώρα και που τον βλέπω, δεν ... είμαι πτώμα.

 

EDIT2:

Α ναι, 2 malloc() κάνω κι ένα 3ο για το string (δεν σου πα τα χω παίξει; :lol:) Για το void * dereference το έχω κάνει έτσι.

Δημοσ. (επεξεργασμένο)
[..]Εδώ θα πρέπει να διαφωνήσω κάθετα με τον φίλο Directx και ελπίζω να μην το πάρει προσωπικά. Καταρχήν η C είναι κάθε άλλο παρά typeless γλώσσα, θα τρίζουν τα κόκκαλα του Ritchie με κάτι τέτοια.[..]

 

Ειλικρινά δεν τίθεται θέμα. Την προσωπική μου γνώμη διατύπωσα επί της βάσης ότι οι τύποι που προσφέρει η C ειδικά όσο προχωράμε προς το παρελθόν ήταν για να παραβιάζονται και είχε κάλο λόγο που επιτράπηκε αυτό από τον Ritchie αρχικά λόγο καταγωγής (βλ. B & και πρώιμες CPU) και ύστερα λόγο πρακτικής χρησιμότητας καθώς μια Low Level / System Level γλώσσα (διότι τελικά αυτό είναι η C) έχει πολύ διαφορετικές ανάγκες από ότι μια για εμπορικές εφαρμογές όπου εκεί υπερέχουν άλλα χαρακτηριστικά και ιδιότητες, όσοι ασχολούνται με System Level προγραμματισμό εκτιμούν τα θετικά του typeless ή του πολύ χαλαρού type-safety του πράγματος.

 

Αυτά, τα πενιχρά από εμένα.

 

Καλή συνέχεια!!

:)

 

EDIT: Όσον αφορά την χρήση "%?" αντί σταθερών τύπων (#DEFINE) σου επιτρέπει να κάνεις ορισμένα ιδιόρρυθμα πραγματάκια όπως για παράδειγμα ένα Stack όπου κάθε στοιχείο του θα μπορούσε να περιλαμβάνει απροσδιόριστο αριθμό αποθηκευμένων τύπων/list-nodes (σε στυλ "%c%d%s" κατά το push και pop) με πολύ κομψό τρόπο.

Επεξ/σία από Directx
Δημοσ.

το φορουμ συνουσιαζεται...

>typedef struct
{
int foo;
int fee;
int bar;
char data[1];
}TheStruct;

void foo_add(TheStruct** bar,void* pData,size_t cbData)
{
*bar = (TheStruct*) malloc((size_t)&((TheStruct*)0)->data + cbData);
memcpy( (*bar)->data,pData,cbData);
}
void foo_get(TheStruct** bar,void* pData,size_t cbData)
{
memcpy(pData,(*bar)->data,cbData);
}
int main(int,char**)
{
TheStruct* ts;
long double ld = 1.001243e10,ld1;
foo_add(&ts,&ld,sizeof(ld));
foo_get(&ts,&ld1,sizeof(ld1));
std::cout<<
	ld1
	;

return 0;

}

 

Δημοσ.

Λοιπόν, σιγά μην δεν ξανα έμπαινα, παρόλο που ειλικρινά σέρνομαι :lol:

 

Ξανακοίταξα τον κώδικα τώρα παπι, και αν τον καταλαβαίνω καλά συνοψίζεται σε αυτό εδώ:

 

>
int main( void )
{
void *pdata = NULL;
int Int = 10;
double Double = 12.8990;

pdata = &Int;
printf("%d\n",  *(int *)pdata );

pdata = &Double;
printf("%g\n",  *(double *)pdata );

return 0;
}

 

ή με χρήση συνάρτησης, σε αυτό...

 

>
/* ----------------------------------------------------------------------- */
void *setdata( void *pvalue, size_t valsize  )
{
void *pdata = malloc(valsize);
if ( !pdata )
	return NULL;

memcpy(pdata, pvalue, valsize);
return pdata;
}
/* ----------------------------------------------------------------------- */
int main( void )
{
void *pdata = NULL;
int Int = 10;

pdata = setdata( &Int, sizeof(Int) );
if ( !pdata )
	return 0;

printf("%d\n",  *(int *)pdata );

free( pdata );
return 0;
}

 

και απλά το έχεις προσαρμόσει στη δομή της στοίβας.

 

Αν ισχύει λοιπόν αυτό (έχω καταλάβει σωστά δηλαδή) το πρόβλημα είναι πως με αυτόν τον τρόπο δεν μπορείς να αναθέσεις απευθείας τιμή στον pdata, δηλαδή...

 

>
void *pdata = setval( 12.345, sizeof( double )  );

 

ή π.χ...

 

>
void *pdata = setval( "Hello world", 11+1  );

 

γιατί οι συναρτήσεις σου περιμένουν address.

 

Πες μου αν το έχω καταλάβει λάθος.

Δημοσ.

Τελικά, πως μπορούμε να υλοποιήσουμε non-variadic void *pata = setval( typeId, value) που να λειτουργεί και με...

 

>
void *pdata = setval( DT_INT, 100 );

και με ...

 

>
void *pdata = setval( DT_STRING, "I am a string" );

 

Ξέρετε κάποιον άλλον τρόπο από αυτόν που έχω βάλει στο πρόγραμμα;

 

Πιο συγκεκριμένα, με τι data-type θα ορίσουμε την παράμετρο value στην συνάρτηση και πως θα την διαχειριστούμε;

Δημοσ.

Και ποιος ο λογος μιας στακ που γεμιζει με constans;

 

btw Αυτο που θα εκανα εγω..

Επειδη η c δεν εχει template κι αν το κανω με macro θα βγει ενα μακαρονι μισκο και κυριος αυτες οι δομες ειναι "bounded" κατα καποιο τροπο.. Εγω δεν θα εγραφα βιβλιοθηκη αλλα ενα code generator happy.gif. Ετσι θα εψω κατι αμεσα και κυριος θα ειναι στα μετρα που το θελω!

Δημιουργήστε ένα λογαριασμό ή συνδεθείτε για να σχολιάσετε

Πρέπει να είστε μέλος για να αφήσετε σχόλιο

Δημιουργία λογαριασμού

Εγγραφείτε με νέο λογαριασμό στην κοινότητα μας. Είναι πανεύκολο!

Δημιουργία νέου λογαριασμού

Σύνδεση

Έχετε ήδη λογαριασμό; Συνδεθείτε εδώ.

Συνδεθείτε τώρα

  • Δημιουργία νέου...